Working with Date ?

I have different date formats and wonder if I can make them the same ... Some are just year but others are either "2005-03-10T00:00:00Z" or "2005-03-10" or "10-03-2005", but all I want at the moment is the year property. Any suggestions ?

Accepted Reply

A little less specific but easier to read, apparently should be this ... "\b(?<year>19|20\d{2})\b"

Replies

I don't know if there is a way to do this with format.


But you could use a Regex to find out the 4 digits


I tried this, with your test cases, it works (cannot format, it changes the code !).


let test1 = "2005-03-10T00:00:00Z"

let test2 = "2005-03-10"

let test3 = "10-03-2005"

var tests = [test1, test2, test3]


let patternY = "[0-9]{4}" // 4 digits

let regexY = try! NSRegularExpression(pattern: patternY)

for test in tests {

if let match = regexY.firstMatch(in: test, range: NSRange(0..<test.utf16.count)) {

let year = String(test[Range(match.range(at: 0), in: test)!])

print(year)

}

}


You get:

2005

2005

2005

It’s hard to make suggestions without a more concrete specification of the problem. To start:

  • Where do these strings come from? Are there any constraints on their format?

  • Specifically, are they all using the Gregorian calendar?

  • Are they guaranteed to have a 4 digit year?

  • Are they guaranteed to use Western Arabic digits digits?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
let patternY = "[0-9]{4}"          // 4 digits

FYI, this will match things you don’t want to match, for example, with the string

12:34:56.987654 10 Mar 2005
, which contains the time in milliseconds, you’ll get
9876
. If you want to search for a sequence of exactly four digits, you need to use something like
(^|\D)(?<YEAR>\d{4})(\D|$)
, that is:
  • Four digits (
    \d{4}
    )
  • Prefixed by nothing or a not digit (
    ^|\D
    )
  • Suffixed by a not digit or nothing (
    \D|$
    )

I’m also using a named capture group (

(?<YEAR>***)
) to make it easier to locate the result.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I still have to learn all the capabilities of regex !


When I type

let patternY = "(^|\D)(?<YEAR>\d{4})(\D|$)"

I get error: invalid escape sequence in literal


What should be the correct pattern ?

Thanks for the replies, I have decided to use regular expression. Even with (^|\D)(?<YEAR>\d{4})(\D|$) returns a dash "-", is there another way to make sure it's a specific length of digits ?

Even with (^|\D)(?<YEAR>\d{4})(\D|$) returns a dash "-",


You may be misusing the pattern.

import Foundation

let pattern = #"(^|\D)(?<YEAR>\d{4})(\D|$)"#
let regex = try! NSRegularExpression(pattern: pattern)
func extractYear(from str: String) -> String? {
    if let match = regex.firstMatch(in: str, range: NSRange(0..<str.utf16.count)) {
        return String(str[Range(match.range(withName: "YEAR"), in: str)!])
    } else {
        return nil
    }
}
print(extractYear(from: "2005-03-10T00:00:00Z") ?? "*no year*") //->2005
print(extractYear(from: "2005-03-10") ?? "*no year*") //->2005
print(extractYear(from: "10-03-2005") ?? "*no year*") //->2005

The capture is named "YEAR", so use `range(withName: "YEAR")`.

Did you test with the simple pattern "[0-9]{4}

This is exactly 4 digits.


Of course, it is not as robust as Quinn solution, so it depends on what you may have as strings.


But, when I use this pattern,

let pattern = #"(^|\D)(?<YEAR>\d{4})(\D|$)"#

I get an error:

Consecutive statements on a line must be separated by ';'

What should be the correct pattern ?

The pattern is not invalid. That message is coming from the Swift compiler, which is trying to interpret the sloshes (

\
) in the pattern as escape sequences (like
\n
or
\t
). To avoid that you must either:
  • Double escape the sloshes

    let patternY = "(^|\\D)(?<YEAR>\\d{4})(\\D|$)"

    .

  • Use a ‘raw’ string:

    let patternY = #"(^|\D)(?<YEAR>\d{4})(\D|$)"#

    .

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks.


Maybe it does not work in playground where I tested (Xcode 10.1ß).


If I type

let patternY = #"(^|\D)(?<YEAR>\d{4})(\D|$)"#


I get

Consecutive statements on a line must be separated by ';'


The other one

let patternY = "(^|\\D)(?<YEAR>\\d{4})(\\D|$)"


works but inserts a dash, as already noted.

Maybe it does not work in playground where I tested (Xcode 10.1ß).

10.1 beta? Why are you using that? Folks here (at least me and OOPer, based on the code in their 26 Apr post) are using Xcode 10.2.x.

The raw literal syntax (see SE-0200) is new in Swift 5, so is definitely not going to work in Xcode 10.1 beta. The older, escaped syntax should work. I’m not sure what’s going on at your end, but I took OOPer’s example from their 26 Apr post, replaced this:

let pattern = #"(^|\D)(?<YEAR>\d{4})(\D|$)"#

with this:

let pattern = "(^|\\D)(?<YEAR>\\d{4})(\\D|$)"

and it worked as expected in my copy of Xcode 10.1 (GM).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

"\D" means anything but not a digit. I refined the date a bit giving it a range and making sure it's 4 I could only do not start with or be followed by a digit ... "(?<!\d)(?<YEAR>20[0-9]{2}|19[8-9]{1}[0-9]{1})(?!\d)"

A little less specific but easier to read, apparently should be this ... "\b(?<year>19|20\d{2})\b"