2

I'm loading a string from a byte array in Swift using the following code

let bytes = data[position...position + length]
guard let let dateString = String(bytes: bytes, encoding: .utf8)
    else {return}

This gives me a valid string, for example "20160714".

Using a DateFormatter I then try and parse a date from the string with the following

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYYMMdd"
let date = dateFormatter.date(from: dateString)

This should work, but date is always nil. When I try the same thing in a playground with a string literal (let dateString = "20160714") it works as expected, i'm thinking it's something to do with the encoding.

Any help much appreciated.

EDIT: I've tried both YYYY and yyyy for year, both return nil

It's also worth noting that the byte array comes from loading a file into memory using

guard let data = try? NSData(contentsOfFile: urlString, 
                             options: .alwaysMapped)
    else {fatalError("Invalid file")}

As requested by @vadian, printing Data(bytes) as NSData in the console gives the following output

{length = 8, bytes = 0x3230313630373134}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Alex Brown
  • 1,613
  • 1
  • 13
  • 23

4 Answers4

0
let dateString = String(bytes: bytes, encoding: .'t)

This gives me a valid string

NO, it gives you Optional<String>

so the compiler will not compile

let date = dateFormatter.date(from: dateString)

That means you have to check what happens between this two lines of code, to find the bug ...

OK, UPDATE with some example where to see the error is not so easy. Try this in Playground and see what it prints!

let str = "0170101"
var data = Data(str.utf8)
// this makes a trouble
data[0] = UInt8(1)
let dateString = String(bytes: data[0 ..< data.count], encoding: .utf8)

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYYMMdd"
dateFormatter.locale = Locale(identifier: "en_US_POSIX")

let date = dateFormatter.date(from: dateString ?? "")

let strd = "170101"
let date2 = dateFormatter.date(from: strd)

print(dateString ?? "", date)
print(strd, date2)

and it prints

170101 nil
170101 Optional(2016-12-31 23:00:00 +0000)

Do you expect that? Try to find the bug elsewhere in you code ...

And see how it present the date if it looks valid. Interesting, isn't it?

Cjange the first line to

let str = "020170101"

and it prints

20170101 nil
20170101 Optional(2016-12-31 23:00:00 +0000)
user3441734
  • 16,722
  • 2
  • 40
  • 59
  • I didn't think i'd have to say that it compiles...in the actual code I unwrap the string, but for simplicity sake I left that out. I'll update my question though to clear up any confusion. – Alex Brown Feb 05 '20 at 11:25
  • So there is the place, where to find your bug ... not at the code you put here. there is no trouble with your dateFormatter portion. – user3441734 Feb 05 '20 at 11:27
  • 1
    @AlexBrown see my update and think, what i am talking about – user3441734 Feb 05 '20 at 11:43
  • This is very helpful and seems to illustrate the root cause of my problem. Converting the string to UTF8 straight from bytes produces (what appears to be) the correct string, but for some reason the DateFormatter doesn't like it. This is inline with what I was seeing in my tests, if I use a string literal with the same value it works as expected, something must be happening with the string conversion... – Alex Brown Feb 05 '20 at 11:52
  • Correct, although if it were bad data I would imagine it would fail at the String parsing level – Alex Brown Feb 05 '20 at 11:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207270/discussion-between-user3441734-and-alex-brown). – user3441734 Feb 05 '20 at 11:57
0

The code that you have written is correct. Also its converting in Date object. Try to print date on console.

let dateString = "20160714"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYYMMdd"
let date = dateFormatter.date(from: dateString)

Just convert your date object in the string format that you want (i.e yyyy/MM/dd)

  • Yes the code is correct which is why i'm confused, the problem seems to be with the formatted string, not the code to parse the date. – Alex Brown Feb 05 '20 at 11:29
0

Try to parse dateString with ISO8601DateFormatter (requires iOS 10.0+, tvOS 10.0+ or macOS 10.12+):

let dateFormatter = ISO8601DateFormatter()
dateFormatter.formatOptions = [.withYear, .withMonth, .withDay]
let date = dateFormatter.date(from: dateString)

Tested with Swift 5.1.3/Xcode 11.3.1.

Roman Podymov
  • 4,168
  • 4
  • 30
  • 57
-1

First convert bytes to string than convert it to valid date

let bytes = data[position...position + length] let dateString = NSString(bytes: bytes!, length: data!.count, encoding: NSUTF8StringEncoding)

let dateFormatter = DateFormatter()

dateFormatter.dateFormat = "YYYYMMdd"

let date = dateFormatter.date(from: dateString)

Anuj Doshi
  • 16
  • 3
  • This doesn't seem to be any different from the code that is already in the question. If there is a specific difference that you believe fixes the problem, can you highlight that? – Jory Geerts Feb 05 '20 at 11:26