Giter Site home page Giter Site logo

akosma / swiftmoment Goto Github PK

View Code? Open in Web Editor NEW
1.6K 44.0 148.0 250 KB

A time and calendar manipulation library for iOS 9+, macOS 10.11+, tvOS 9+, watchOS 2+ written in Swift 4.

Home Page: http://akosma.github.io/SwiftMoment

License: BSD 2-Clause "Simplified" License

Swift 91.21% Ruby 7.66% Objective-C 0.43% Shell 0.60% Dockerfile 0.10%

swiftmoment's Introduction

SwiftMoment

Join the chat at https://gitter.im/SwiftMoment/Lobby Build Status CocoaPods Compatible Carthage Compatible Platform swiftyness

This framework is inspired by Moment.js. Its objectives are the following:

  • Simplify the manipulation and readability of date and interval values.
  • Provide help when parsing dates from various string representations.
  • Simplifying the formatting of date information into strings.
  • Streamlining getting date components (day, month, etc.) from dates and time intervals.

Important: This framework supports iOS 9+, macOS 10.11+, tvOS 9+, watchOS 2+, Xcode 8 and Swift 3.

Installation

SwiftMoment is compatible with Carthage and CocoaPods. With CocoaPods, just add this to your Podfile:

pod 'SwiftMoment'

SwiftMoment can also be used via the Swift Package Manager. Just add it to the dependencies in your Package.swift file:

let package = Package(
    name: "MyPackage",
    dependencies: [
        ...
        .package(url: "https://github.com/akosma/SwiftMoment.git", from: "0.7.1"),
    ],
    ...
)

Mac OS X Notes

  • Drag the created .framework file into the Xcode Project, be sure to tick 'Copy Files to Directory'
  • In the containing applications target, add a new 'Copy File Build Phase'
  • Set the 'Destination' to 'Frameworks'
  • Drag in the created .framework

Examples

To use this library, just import SwiftMoment in your application.

To create new moment instances:

let now = moment()
let yesterday = moment("2015-01-19")

By default, moments are initialized with the current date and time. You can create moments for any... moment in the future or the past; you can do that by passing strings in different formats:

let yesterday = moment("2015-01-19")

You can also do it by directly specifying the components manually:

let today = moment([2015, 01, 19, 20, 45, 34])

You can also use a dictionary with the following keys:

let obj = moment(["year": 2015,
                    "second": 34,
                    "month": 01,
                    "minute": 45,
                    "hour": 20,
                    "day": 19
                ])

When using a [String: Int] dictionary, the order of the keys does not matter. Moreover, only the keys above are taken into account, and any other information is ignored.

There is also an extension to the Int type in Swift, used to create Duration values directly from an integer value:

let duration = 5.hours + 56.minutes

Architecture

The two most important components of this library are the Moment and Duration structures. Moment wraps an NSDate instance, while Duration wraps an NSTimeInterval value.

Both Moment and Duration comply with the Comparable protocols, and include all the required operators. In addition, Moment instances can be subtracted from one another (which yields a Duration) and Duration instances can be added to Moments to create new moments.

Moments and Durations are made as immutable as possible.

Tests

Swift Moment includes a suite of tests showing how to use the different functions of the framework.

To run the Linux tests in a macOS environment, please use the included Dockerfile:

docker build --tag swiftmoment .
docker run --rm swiftmoment

Playground

A playground is included in the project to learn how to use the library.

Differences with Moment.js

  • Format strings DD and dd do not yield the same results.

Contributors

Lots of people are actively helping in the development of this library; please check the CONTRIBUTORS file for the full list! Thanks to all :)

License

This project is distributed under a BSD license. See the LICENSE file for details.

swiftmoment's People

Contributors

andersklenke avatar andrewbranch avatar armstrongnate avatar burak-akkas avatar chrissloey avatar dcestari avatar fabfelici avatar fespinoza avatar fpillet avatar getaaron avatar gitter-badger avatar goncalvesa avatar hlandao avatar karupanerura avatar kengoldfarb avatar lbrndnr avatar liooo avatar madhavajay avatar mihyaeru21 avatar nsainaney avatar pine avatar toadzky avatar vandyshev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftmoment's Issues

Unit tests are time zone specific

testCanCreateWeirdDateFromComponents() and testFormatDates() fail if run in Chicago, IL because the expected result is time zone-specfic. This is because NSDateFormatter uses the local time zone.

Formatting time

I got a timestamp which I wrapper in Duration object

var duration = Duration(value: waitingTimeMillis / 1000)

This produces a valid result like 41:00 or 1:41:59. Now I want to be able to format this into something more pleasant and simple like 41' or 1h 41'.

How would I do that?

Subtracting months edge case - subtracts 30 days

It seems to subtract 30 days instead of 1 whole month.

Detailed Description

If the starting date is 1/30/2018, and I subtract 1 month, it becomes 12/31/2017, I would have expected 12/30/2017. If I subtract 2 months, it becomes 12/01/2017. I would have expected it to be 11/30/2017.

Your Environment

  • Version used: 0.7
  • Operating System and version: latest version of Swift, Xcode

fromNow() returning ""

This part is messing around

    private func NSDateTimeAgoLocalizedStrings(key: String) -> String {
      // get framework bundle
      let bundleIdentifier = "com.akosma.SwiftMoment"
      guard let frameworkBundle = NSBundle(identifier: bundleIdentifier) else {
        return "" <<----
      }

PS: I'm using pods

Minute function only returns a single digit + Additional Helpful Variables

If the time is 12:00 for example, the minute only returns a single digit.
In order to get around it, I did the following:
String(format: "%02d", startDate!.minute)

Also, idk if you'd like me to submit a pull request, but here are a few useful variables I added for 12hour support:

public var twelveHour: Int {
        if hour > 12 && hour != 24 {
            return hour - 12
        } else {
            return hour
        }
    }
public var twelveHourPeriod: String {
        var period = String()
        if hour >= 12 && hour != 24 {
            period = "PM"
        } else if hour < 12 || hour == 24{
            period = "AM"
        }
        return period
    }

Problem with date parsing

Problem with parsing time format

  • "h:mm:ss A",
  • "h:mm A",

Detailed Description

According to Apple's ISO descriptions, A represents microseconds, as opposed to AM/PM.
Those formats should be changed to a rather than A.

Context

This causes issues for people trying to use SwiftMoment to represent times.

Possible Implementation

Change the formats to the following:

let formats = [
    isoFormat,
    "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'",
    "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'",
    "yyyy-MM-dd'T'HH:mm:ss.SSSZ",
    "yyyy-MM-dd",
    "h:mm:ss A",
    "h:mm A",
    "h:mm:ss a",   <---
    "h:mm a",      <---
    "MM/dd/yyyy",
    "MMMM d, yyyy",
    "MMMM d, yyyy LT",
    "dddd, MMMM D, yyyy LT",
    "yyyyyy-MM-dd",
    "yyyy-MM-dd",
    "GGGG-[W]WW-E",
    "GGGG-[W]WW",
    "yyyy-ddd",
    "HH:mm:ss.SSSS",
    "HH:mm:ss",
    "HH:mm",
    "HH"
]

Your Environment

  • Version used: Latest
  • Operating System and version: iOS 9.0+
  • Link to your project:

Swift 3 compatibility?

Has anyone tried using this framework in a Swift 3 project? Does it auto-convert without issues?

Function Moment.add(value: Int, _ unit: TimeUnit) -> Moment goes wrong.

Line 413 in Moment.swift will cause unexpected result.

cal.timeZone = NSTimeZone(abbreviation: "UTC")!

For example:

I have a date: "2016-07-01 00:00:00 GMT+08:00", then I called date.add(1, .Months), I expect to get "2016-08-01 00:00:00 GMT+08:00", but I only got "2016-07-30 16:00:00 GMT+00:00", which is "2016-07-31 00:00:00 GMT+08:00". You just convert my original date to "2016-06-30 16:00:00 GMT+00:00", then add a month. Bang!

I modified this line as below:

cal.timeZone = timeZone
cal.locale = locale

Then it went right, but I don't know is there any other errors it would cause.

Needs more documentation...

Moment.js users will understand the benefits of this library but others may not get it. More examples in the README and doc is needed.

EndOf Years bug

There's a bug when getting EndOf a year

Detailed Description

let now = moment()
let endYear = now.subtract(1, .Years).endOf(.Years)  // 2016-12-30 23:59:59

expected result is 2016-12-31 23:59:59

timezone is ignored in the iso8601 conversion from string to date

SwiftMoment ignores the timezone from an iso8601 string formatted date even if it's specified.

Example:
The code
print(moment("2017-02-09T03:17:52.477Z")!.format("yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX")) displays: 2017-02-09T03:17:52.477-05:00
the expected output is either 2017-02-09T03:17:52.477Z or 2017-02-08T22:17:52.477-05:00

from Wikipedia: If the time is in UTC, add a Z directly after the time without a space. Z is the zone designator for the zero UTC offset. "09:30 UTC" is therefore represented as "09:30Z" or "0930Z". "14:45:15 UTC" would be "14:45:15Z" or "144515Z".

Comparison of distant moments crashes

Comparison of distant moments crashes

Detailed Description

moment() - 50.years - 50.years - 50.years - 50.years - 50.years > moment()

(yes 250.years crashes but it's another story)

Context

Obvious, compare distant dates

Possible Implementation

Remove the Int() line 682

Time Zone not changing

I'm trying to create a moment with GMT-0300 date, from a GMT date string with format "yyy-MM-dd'T'HH:mm:ss" by doing
let timeZone = TimeZone(secondsFromGMT: -10800)
let locale = Locale(identifier: "en_US_POSIX")
let teste = moment(dateString, dateFormat: "yyy-MM-dd'T'HH:mm:ss", timeZone: timeZone!, locale: locale)

For example:
dateString = "2016-10-12T10:02:50"
And I want my moment to store hour = 7, minute = 02

No matter which TimeZone I use to create it, it always returns the time that was given in dateString. I couldn't find any documentation on this. Is there any way to to do so?

converting timezone issue

Hi guys, just a quick question.
Is there any way i can put timezone into format(), i need to do some timezone conversion and not sure how to do it. Thanks.

support for Calendar Time

Will it be included ?

moment().subtract(10, 'days').calendar(); // 11/17/2015
moment().subtract(6, 'days').calendar(); // Last Saturday at 6:18 PM
moment().subtract(3, 'days').calendar(); // Last Tuesday at 6:18 PM
moment().subtract(1, 'days').calendar(); // Yesterday at 6:18 PM
moment().calendar(); // Today at 6:18 PM
moment().add(1, 'days').calendar(); // Tomorrow at 6:18 PM
moment().add(3, 'days').calendar(); // Monday at 6:18 PM
moment().add(10, 'days').calendar(); // 12/07/2015

http://momentjs.com/

Bug in endOf in any months with 31 days

Detailed Description

When I use the moment().endOf("M") in many 31 days months, as December, the return is the day 30th.

moment().endOf("M").format("dd/MM/YYYY") return 30/12/2017

Context

I use this to show the start and end of a month report

Environment

  • Version used: 0.7

Check non EN language support for Regions

@twairball pointed out that 2 letter Language abbreviations won't work with other languages like zh-Hant_US?

We should look at a better way to solve this problem.
I also don't know if the Bundle solution is the best solution either.
It might be better to consider i18n strings.

fromNow() returning ""

This part is messing around

guard let frameworkBundle = NSBundle(identifier: bundleIdentifier) else {
    return ""
}

bugs in endof

A bug in endof month (especially February 2017)?

print("endof m", moment().endOf(TimeUnit.Months).date.toDateTime())
print("start of next -1", moment().add(1, TimeUnit.Months).startOf(TimeUnit.Months).subtract(1, "d").date.toDateTime())

The results are
3/2/17, 11:59 PM
and
2/28/17, 12:00 AM

I am with Version 0.6

French translation strings

Hello,

I'm using your library to display the time between the last modification date of a file and the current date.

It works very well but it seems French strings (and maybe others) are not loaded properly.
I'm not sure, but I think that each translation string keys containing "%@" are not loaded.
You can see it the the screenshot bellow:
img_2852

Just in case, here is the code used for the detailLabel:

detailLabel.text = moment(node.lastSyncDate).fromNow()

I'm using Swift 3 / Xcode 8.1 / iOS 10.1

Hope you can help me, cheers for the good work.

Swift 2.3 compatibility

It is needed to support swift 2.3 and I found out that just replacing codes in MomentFromNow.swift#104 to

#if swift(>=2.3)
  let path = NSURL(fileURLWithPath:resourcePath).URLByAppendingPathComponent(bundleName)!
#else
  let path = NSURL(fileURLWithPath:resourcePath).URLByAppendingPathComponent(bundleName)
#endif

makes XCode 8 build fine.

So i'm ready for pulling request.

Which is better?

  1. Support both version (swift2.2 and swift2.3) in master branch using #ifswift like above.
  2. Make another branch swift2.3 and split build environment into Xcode 7 and Xcode 8

can't compile in Xcode 8.1

I use carthage. The version 0.7 can't be built but 0.6 works.

Here is the failure output

MomentTests.swift:13:1: error: Type Body Length Violation: Type body should span 350 lines or less excluding comments and whitespace: currently spans 400 lines (type_body_length)

Get Tomorrow's date

I'm trying to get tomorrow's date like this, but it just returns today's.

moment().add(1, "days").format("MMMM do")

This works fine in Javascript
moment().add(1, "days").format("MMMM Do")

Your Environment

Swift 4.0
Xcode Version 9.2 (9C40b)

Duration: Printable results differ by date, causing testDurationDescription() to fail in February

From the NSDateComponentsFormatter docs:

NSDateComponentsFormatter will calculate as though counting from the current date and time (e.g. in February, 1 month formatted as a number of days will be 28)

So the result of Duration's description() method will differ depending on the current time. This causes testDurationDescription() to fail in February, when a month only has 28 days.

There are two solutions.

Set allowedUnits

This could be resolved by setting allowedUnits:

formatter.allowedUnits = .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit

But the output is less pretty:

173d 7:04:00

vs

5m 3w 1d 7:04:00

Document the existing behavior and build in more tests

As an alternative, we could simply document this behavior, and either delete this unit test or build in some flexibility.

Device using Traditional Chinese in Taiwan (zh-Hant-TW) returning empty string

Detailed Description

getLanguageBundle is returning nil. Strangely, locale.identifier is returning zh-TW, without the "Hant".

The 2nd part of getLanguageBundle therefore only get the language code as zh.

Since now the bundle only has zh-Hant, no localized string is found.

Context

Localization does not work for Taiwan app users.

Possible Implementation

I believe another fallback using Bundle to load will work.

Your Environment

  • Version used: 0.7
  • Operating System and version: 11.2
  • Link to your project: -

Unexpected value when adding months.

When trying to add months to an instance of Moment I get an unexpected value. I would expect the following example to return an instance of Moment representing the 1st of June 2016 but instead it returns 31st of May 2016.

    let beginOfMonth = moment([2016, 5, 1, 0, 0, 0])!
    let nextMonth = beginOfMonth.add(1, .Months))

Wouldn't it make more sens to add value to the Month component of NSDate using NSDateComponents instead of simply adding 30 days?

Inefficient use of NSDateFormatter

Instantiate NSDateFormatter is expensive operation and you use it exactly that way. You can read this in Apple documentation:

Creating a date formatter is not a cheap operation. If you are likely to use a formatter frequently, it is typically more efficient to cache a single instance than to create and dispose of multiple instances. One approach is to use a static variable.

How to get the UTC time?

In momentjs, we simply do moment().utc().format(), how would we accomplish this in SwiftMoment?

For completion, this is what I ended up doing using Date() and TimeFormatter()

    let UTCDate = Date()
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
    formatter.timeZone = TimeZone(secondsFromGMT:0)
    let utcTime = formatter.string(from: UTCDate)

Thank you

`fromNow()` not returning localized string

Detailed Description

Using fromNow() did not return the localized version for "%d days ago" and others.

The string format used in fromNow() is %%1.0f %@days ago, but in the strings files all uses %d and not %f, therefore it never get matched.

Context

Seems like a broken change.

Possible Implementation

Change the format to %d days ago, and use value as Int.

Your Environment

  • Version used: 0.7.0
  • Operating System and version: Mac 10.12.3
  • Link to your project: -

toNow() feature ?

Is it already possible to treat with future dates like we do past dates with fromNow() -> ".. ago" ?

eg. "tomorrow", "in 5 seconds", "in 2 months", "in 4 years"

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.