ISO8601dateformatter 1970-01-01 00:00:00 issue - swift

How to deal with the new year and ISO8601 returning last year as year component.
To my horror, I realized ISO8601DateFormatter was returning 1977 as a year to the 1978-01-01 00:00:00
It took a while to realize this. That turned out is not wrong. Nonetheless, given the specific year of 1978, for the formatted to return 1977 is shocking.
I don't even need the timestamp. How can I reliably retrieve the specified year without having to add a second to every calendar date?
import Foundation
let datestring = "1978/1/1"
var formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone(abbreviation: "UTC")
formatter.formatOptions = [.withFullDate]
let date2 = formatter.date(from: datestring) ?? Date()
print(date2)
var calendar = Calendar(identifier: .iso8601)
var year = calendar.component(.year, from: date2)
var month = calendar.component(.month, from: date2)
var day = calendar.component(.day, from: date2)
var era = calendar.component(.era, from: date2)
print("year \(year) month \(month) day \(day) era: \(era)")
===
1978-01-01 00:00:00 +0000
year 1977 month 12 day 31 era: 1

By default the Calendar instance will have your local timeZone. You can see this by printing print(calendar.timeZone.abbreviation() ?? "UNKNOWN"). In my case (in Seattle, WA, USA) it prints "PDT". If you simply set your calendar timezone to UTC it prints exactly what you expect:
year 1978 month 1 day 1 era: 1

Related

Swift date components incorrect return of date day number

I need to obtain a date from some variable values
So I specify year, month and day and I need a Date as return
Doing the following works except for the day because it return the day input - 1
let todayDate: Date = Calendar.current.startOfDay(for: Date.from(year: 2022, month: 09, day: 05)!)
print("today date = \(todayDate)")
extension Date {
static func from(year: Int, month: Int, day: Int) -> Date? {
let calendar = Calendar(identifier: .gregorian)
var dateComponents = DateComponents()
dateComponents.year = year
dateComponents.month = month
dateComponents.day = day
return calendar.date(from: dateComponents) ?? nil
}
}
And the output is
today date = 2022-09-04 22:00:00 +0000
Date and time can be a bit tricky. The Date struct stores a point in time relative to GMT. If you print it it will show exactly that.
Solution:
Don´t use print, use a proper Dateformatter. To illustrate what I mean use this in a playground:
let date = Calendar.current.startOfDay(for: Date())
print(date)
//2022-09-03 22:00:00 +0000
// when it is 4.th of september 00:00 in my timezone (+- Daylight saving) it is this time in GMT
let formatter = DateFormatter()
formatter.dateFormat = "dd MM yyyy HH:mm:ss"
print(formatter.string(from: date))
//04 09 2022 00:00:00
// this is the time in my timezone
So the issue here is not that it has the wrong time, it is just not presented in the correct time zone.

week range in swift with weekYear

Could you help me, I don't know what configuration I'm missing
I want to obtain the week range for the year 2021, week 37 in Mexico that starts from Monday to Sunday but it seems that my code brings another week number and also starts from Sunday to Monday
The week in Mexico starts from
Week 37 September 13, 2021 September 19, 2021
var weekNumber: Int = 0
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 2
calendar.minimumDaysInFirstWeek = 4
let year = calendar.component(.year, from: Date())
weekNumber = calendar.component(.weekOfYear, from: Date())
print("weekNumber",weekNumber)
print("Date",Date())
let startComponents = DateComponents(weekOfYear: weekNumber, yearForWeekOfYear: year)
let startDate = calendar.date(from: startComponents)!
let endComponents = DateComponents(day:7, second: 0)
let endDate = calendar.date(byAdding: endComponents, to: startDate)!
print("startDate",startDate)
print("endDate",endDate)
You are using the wrong calendar. The first weekday of Gregorian calendar is Sunday, what you need is to use ISO8601 calendar. Also when using weekOfYear you would use yearForWeekOfYear:
let now = Date()
var calendar = Calendar(identifier: .iso8601)
let yearForWeekOfYear = calendar.component(.yearForWeekOfYear, from: now)
let weekNumber = calendar.component(.weekOfYear, from: now)
print("weekNumber", weekNumber)
print("Date", now.description(with: .current))
let startDate = DateComponents(calendar: calendar, weekOfYear: weekNumber, yearForWeekOfYear: yearForWeekOfYear).date!
let endDate = calendar.date(byAdding: .weekOfYear, value: 1, to: startDate)!
print("startDate", startDate.description(with: .current))
print("endDate", endDate.description(with: .current))
This will print:
weekNumber 37
Date Sunday, September 19, 2021 at 9:07:02 PM Brasilia Standard Time
startDate Monday, September 13, 2021 at 12:00:00 AM Brasilia Standard Time
endDate Monday, September 20, 2021 at 12:00:00 AM Brasilia Standard Time

GMT to local time conversion ERROR with swift UIKIT

Does anyone have an answer to why I am getting -8 hours instead of -7 (my timezone) hours difference upon converting time from GMT to local i using the formula below:
print("TIMEZONE IN HOURS: \(timeZone/3600)")
let interval = Date(timeIntervalSinceReferenceDate: 93866.4142533315)
print("GMT TIME: \(interval)")
let intervalDateComponents = calendar.dateComponents([.hour, .minute, .second], from: interval)
let mHour = intervalDateComponents.hour ?? 0
let mMinute = intervalDateComponents.minute ?? 0
print("INTERVAL DATE COMPONENT: \(intervalDateComponents)")
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let intervalDateString = formatter.string(from: interval)
print("DATE STRING FORMAT: \(intervalDateString)")
prints out the following: (please pay attention to date)
TIMEZONE IN HOURS: -7
GMT TIME: 2001-01-02 02:04:26 +0000 (January 2)
INTERVAL DATE COMPONENT: hour: 18 minute: 4 second: 26 isLeapMonth: false
DATE STRING FORMAT: 2001-01-01 18:04:26 (January 1)
interval HOURS: 18
edit/update:
let mDateString = formatter.string(from: interval)
print("DATE STRING FORMAT: (mDateString)")
let mdate = formatter.date(from: mDateString)
print("MDATE: (mdate)")
let mDateComponents = calendar.dateComponents([.hour, .minute, .second], from: mdate!)
print("M DATE COMPONENT: (mDateComponents)")
prints out:
DATE STRING FORMAT: 2001-01-01 19:01:27 -0700
MDATE: Optional(2001-01-02 02:01:27 +0000)
M DATE COMPONENT: hour: 18 minute: 1 second: 27 isLeapMonth: false
If you notice above in the code my intention is to extract the local time component to trigger alarm at a later time daily. So I fed the string that shows
2001-01-01 19:01:27 -0700
I converted the string to mdate in order to get time components that I need to use for the alarm to work on time so I got (hour: 18) rather than hour: 19 which I used to generate the mdate string.
The problem there is that you are getting the timezone offset for today. You need to get the timezone offset for the same date you are using:
let timeZone = TimeZone.current.secondsFromGMT(for: interval)
print("TIMEZONE IN HOURS: \(timeZone/3600)")

Swift date manipulation - strange month return

I'm trying to do some date manipulation with Swift and I'm getting an unexpected result. The webservice will pass in a string date, and then I want to get that month and the previous month. I'm using this code (with input grab and such removed):
import Foundation
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(identifier: "America/New_York")
formatter.dateFormat = "yyyy-MM-dd"
let date = formatter.date(from: "2018-12-01")!
let prev = Calendar.current.date(byAdding: .month, value: -1, to: date)!
formatter.string(from: date)
formatter.string(from: prev)
So I've got a valid date, and then I subtract a month from it. The first formatted date shows my expected 2018-12-01 but then on the second line, instead of saying 2018-11-01 it says 2018-10-31.
I'm in PST, which is of course 3 hours ahead of EST. If I add 3 hours I'd get the expected strings. However, since both the input and the output strings were done with a formatter using the timezone, why don't I get the expected output?
The problem is that Calendar.current is in a different timezone (for you) than the formatter.
So date is December 1, 2018 at midnight New York time. But that is November 30, 2018 at 9pm local time (PST) for you.
When you subtract one month it is done in local time (Calendar.current) so you get October 30, 2018 at 9pm. Then you format that date to New York time and it results in October 31, 2018 at midnight.
To get the proper results you want a Calendar in the same timezone as the formatter:
var cal = Calendar(identifier: Calendar.current.identifier)
cal.timeZone = formatter.timeZone
let prev = cal.date(byAdding: .month, value: -1, to: date)!
This will give the expected result.

storing date in coredata swift

I have a textfield connected to a date picker
I am then trying to store the selected date into core data,
My log off the date picked by the user seems ok:
2016-01-29 00:00:00 +0000 [I strip the time component with some code]
This is converted into a String and displayed in the textfield called startDate.
func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM YYYY"
startDate.text = dateFormatter.stringFromDate(sender.date)
}
Now the strange thing is that when I try and store this into CoreData and convert the string back into a date (the attribute I am saving it into is configured as a Date)
let cont = self.context
let newCustomer = NSEntityDescription.entityForName("Customer", inManagedObjectContext: cont)
let aCust = Customer(entity: newCustomer!, insertIntoManagedObjectContext: cont)
let DF = NSDateFormatter()
DF.dateFormat = "EEE, dd MMM YYYY"
aCust.c15das = DF.dateFromString(startDate.text!)
print("Saved Date: \(DF.dateFromString(startDate.text!))")
Now the log prints out:
2015-12-25 00:00:00 +0000
Why the difference? How can I stop this happening?
Sorry if its something obvious that I am not spotting.
"EEE, dd MMM YYYY" -> YYYY: "Week of Year Calendar", aka "ISO Week Date System". The first week does not start on the first January. If the January 1st is either Monday, Tuesday, Wednesday or Thursday, the whole week is the first week of the new year. if it is Friday, Saturday or Sunday the week is the 53rd week of the last year. So this is a calendar with year that only have integral weeks. Either 52 or 53, rather than 365 or 366 days.
In this calendar January 29th would be the 5th day of the 4th week of the year 2016 — 2016-W4-5. This system does not know months and therefor your date is nonsense.
You want "EEE, dd MMM yyyy", as yyyy indicates a year that starts on 1st of January and ends after 31st of December — The Gregorian Year.
[I strip the time component with some code]
You shouldn't do that. Rather NSCalendar's method to get a date at the beginning of the day.
var today: NSDate?
cal.rangeOfUnit(.Day, startDate: &today, interval: nil, forDate: date)
Try this code, that worked for me:
let dateString = "Fri, 29 Jan 2016" // change to your date format
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE, dd MMM yyyy"
dateFormatter.timeZone = NSTimeZone(name: "UTC")
let date = dateFormatter.dateFromString(dateString)
print(date!)