I'd like to know if Date() always init UTC date.
Example:
App is running in location UTC-8
Call Date() => date in UTC or UTC-8?
Thanks.
Date() initiates a single point in time, independent of any particular calendar system or time zone. Date values represent a time interval relative to an absolute reference date.
So the displayed value will be different depending on timezone, e.g.
01/01/2020T00:00:00 in UTC
will be presented as
31/12/2019T23:00:00 in UTC+1
Related
To my understanding let date = Date() would create a date object with my current timezone. I've seen that there are certain functions where I would use a date formatter to then convert it into UTC time and add the offset to that time and then convert back into a date object but I'm wondering if there is a more elegant solution? And if not is there any other way to get the current time in another timezone in a date object without using the data formatter, as I require the calendar.component(.weekday, from: date) to find the date of the week.
If you look at the very first line on the documentation on Date, you can read the following:
A specific point in time, independent of any calendar or time zone.
So your two options when you want to deal with a local date are the DateTimeFormatter and Calendar
I have erroneously used Calendar.current.startOfDay(for: Date()) to populate a date attribute in Core Data. This means that when users cross different timezones I may have different dates unintentially stored in the date attribute field e.g.
Timezone 1 - 25th 23:00
Timezone 2 - 25th 22:00
Timezone 3 - 26th 05:00
I need to update the Calendar to use UTC Timezone but I need to also perform a migration so that the existing entries in Core Data read like this…
Result:
Timezone 1 - 26th 00:00
Timezone 2 - 26th 00:00
Timezone 3 - 26th 00:00
What are the steps to perform this migration. If I do a UTC startOfDay on it Timezone 1 would get 25th 00:00 instead of 26th 0:00 which is what it should be. Is it possible to accurately update existing entries?
Edit:
For some context I need a reliable way to get all the entries for the 26th for example. I used startOfDay to store the date as it meant I could query by it too and have the relevant entry returned (at any moment in time get the startOfDay and it will give me the entries for the whole day). For historical dates I can do the same - let's say the user has navigated back 2 days I can take startOfDay and subtract 2 days using Calendar.current.date(byAdding: .day, value: -2, to: date) and query for that.
So now the timezone breaks the above logic but is there some way to fix this? If I loop through the entries I can figure out the date it was supposed to be for and perhaps change the attribute to a string - e.g. 26-05-2021 or start to store day, month, year instead and query that.
From reading your answer Duncan I don't think I want to use UTC calendar as it would start to store the entry against the incorrect date from the users perspective dependent on their timezone e.g. user moves to next day and utc is still on previous.
Edit 2:
In a migration I will take the date that is stored and map it to new day, month and year properties storing those instead by getting them from Calendar.current.dateComponents([.day, .month, .year], from: date). Then instead of query by date I will query by day month and year of the Calendar.current where the user is. The side effect here is there is potential the user adds something for today (27th) changes timezone and sees 26th data but I don't think it can be avoided and the old data will then show as intended.
If you took the current time and used Calendar.current.startOfDay(for: Date()) to calculate midnight in the user's local time zone, you have a loss of information. You don't know what time of day the operation was performed. If you saved the time of day in the local time zone in another field, you could reconstruct a Date in UTC.
It isn't clear that what you did was wrong. The day, month, and year is only meaningful in a specific time zone. I am in the Washington DC metro area. We are in daylight savings time (EDT). It is currently 20:56 on the 26th of May. However, it's 1:56 AM on the 27th of May in London, 2:57 AM in Munich, and 3:57 AM in Tel Aviv. All at the exact same instant in time. In UTC it is 0:57 AM on the 27th of May.
Most people think of the calendar date in their local time zone. That is their frame of reference. If you ask me the date right now, I'll tell you it's the evening of the 26th of May. Unless I know you are in a different time zone, that's the "right" answer to me.
If I start out at midnight on a given day in my time zone, calling Calendar.current.startOfDay(for: Date()) each hour, I'll get midnight that day for all 24 hours in my local time zone. For the first 20 hours of the day, that would be the same result I would get if I created a Calendar in UTC and made the same call. However, at 20:00 EDT, I would start getting the next calendar day if I made the same query in UTC.
If you don't know what time of day you made the call to Calendar.current.startOfDay(for: Date()), there is no foolproof to figure out the day/month year in UTC at the instant you made the call. It depends on the time of day in the local timezone, and that timezone's offset from UTC.
Consider this code:
var calendarUTC = Calendar(identifier: .gregorian)
if let utcTimeZone = TimeZone(identifier: "UTC") {
print("Valid time zone")
calendarUTC.timeZone = utcTimeZone
}
print ("Start of day in UTC is \(calendarUTC.startOfDay(for: Date()))")
print ("Start of day in local time zone is \(Calendar.current.startOfDay(for: Date()))")
That outputs:
Start of day in UTC is 2021-05-27 00:00:00 +0000
Start of day in local time zone is 2021-05-26 04:00:00 +0000
That's because right now, which is 20:56 on 26 May in my time zone, it's 0:56 on 27 May in UTC. So if I ask the UTC calendar for the start of day for now (Date()) I get midnight on 27 May, in UTC.
If I ask the same question of my local calendar, I get midnight on 26 may in my time zone, which is 4:00 AM on 26 May in UTC.
If I ran the same code this morning at 8:00 AM in my time zone, I would have gotten the output:
Start of day in UTC is 2021-05-26 00:00:00 +0000
Start of day in local time zone is 2021-05-26 04:00:00 +0000
(Since at 8:00 AM on 26 May in EDT is also 26 May in UTC.)
It's tricky and not 100% reliable and only works if you know that all days were created using startOfDay. But first you need to decide what you want. Say one date was created at 10pm in the New York, and one at exactly the same moment in London, at 4am the next day. What day do you want to be stored?
If your date stored is 25th, 10pm, then you know it was created in a timezone where the day started at 10pm UTC. You are lucky, there are only two time zones that would have created this, one without DST, one with DST. So you know it happened in one of these two time zones, within 24 hours.
Unfortunately, time zones cover 26 hours. Fortunately, only some islands in the Pacific Ocean have same time and different dates (+13 and -11 hours). For these places, you cannot possibly know which date is correct, but very few people would be affected.
The DateTime created by DateTime.parse seems to always returns 0 for "timeZoneOffset"
I create a ISO8601 string here in a non UTC timezone: https://timestampgenerator.com/1610010318/+09:00
I pass that string to DateTime.parse:
DateTime date = DateTime.parse("2021-01-07T18:05:18+0900");
Issue: timeZoneOffset is 0 when I was expecting +0900.
print(date.timeZoneOffset); --> 0:00:00.000000
print(date.timeZoneName); --> UTC
print(date); --> 2021-01-07 09:05:18.000Z
Dart DateTime documentation (https://api.dart.dev/stable/2.10.4/dart-core/DateTime/parse.html):
The result is always in either local time or UTC. If a time zone offset other than UTC is specified, the time is converted to the equivalent UTC time.
Why is timeZoneOffset 0? What string do I need to pass to DateTime.parse to get it to store the time in local time instead of converting it to the equivalent UTC time?
The Dart SDK does not really handle different timezones which is the reason why the parse want local timezone (which is the timezone on the system running the program) or UTC.
If you are trying to parse a timestamp without any timezone information, Dart will assume the time are in local timezone (I am in Denmark which are using the Romance Standard Timezone):
void main() {
print(DateTime.parse("2021-01-07T18:05:18").timeZoneName); // Romance Standard Time
print(DateTime.parse("2021-01-07T18:05:18+0900").timeZoneName); // UTC
}
You can convert your UTC timestamp into localtime by using .toLocal() on the timestamp. But again, this will just convert it into the timezone which are on your own system and not the one coming from the time you have parsed:
void main() {
print(DateTime.parse("2021-01-07T18:05:18+0900").toLocal().timeZoneName); // Romance Standard Time
}
If you want to handle time with timezone data you should look into the package timezone: https://pub.dev/packages/timezone
Some notes about saving timezone offset
You should be aware that in most cases, it does not really makes sense to save the time in a form where you can get the original offset again. The problem is that most countries have rules like DST or the user are traveling and does expect the system to handle the time correctly.
So in a lot of cases, the user does not really expect to get the same offset back again but want the time to be with the currently offset based on location and time right now.
The timezone package does e.g. not allow you to parse a timestamp and save the offset together with it (because an offset is not the same as a location). Instead, it want you to specify the location the timestamp are used for so it can calculate the current offset for that location.
So in general, I recommend you to always save time as UTC on storage. When the data are going to be used, you should have some way to know the location of the receiver (e.g. ask the user in some form of profile) and convert the time to that location using the timezone package. If the application are running on a device owned by the receiver of the data, you can convert the UTC to local time using .toLocale().
Bear with me, I'm a newbie to iOS development; I'm currently trying to get the current time in UTC based on the device's local time, and set it in an if / else statement based on the whatever the current time in UTC is.
I've tried the below:
let UTCdate = Date()
if (UTCdate >= 13 && UTCdate < 23 {
// do something
}
It's giving me the error that "Binary operator '>=' cannot be applied to operands of type 'Date' and 'Int'
I know I'm doing something wrong, just don't know what. Apologies if this has already been asked. I google'd to the best of my abilities.
You need to extract the hour from your date. Date is a timestamp, not an hour. And you need to make sure you extract the hour in the UTC timezone and not locale time.
The following will do what you need:
// Create a calendar with the UTC timezone
var utcCal = Calendar(identifier: Calendar.current.identifier) // or hardcode .gregorian if appropriate
utcCal.timeZone = TimeZone(secondsFromGMT: 0)!
// The current date
let date = Date()
// Get the hour (it will be UTC hour)
let hour = utcCal.component(.hour, from: date)
if hour >= 13 && hour < 23 {
}
If you just use the current calendar to extra the hour from date you will get the hour in local time. That is the reason we created a new calendar specific to the UTC timezone (since that is your requirement).
Based on some comments you may not actually want UTC. Simply set the timeZone to whatever timezone you actually need.
If you want Chicago time, use:
utcCal.timeZone = TimeZone(identifier: "America/Chicago")!
And an alternate to creating your own calendar instance is to use:
let hour = Calendar.current.dateComponents(in: TimeZone(secondsFromGMT: 0)!, from: date).hour!
Again, use whatever timezone you need.
You said "I'm currently trying to get the current time in UTC based on the device's local time". You seem not to understand a key thing about Date objects in Cocoa. A Date is an instant in time, anywhere on the planet. It doesn't have a time zone. Internally it's represented as an offset from a moment in time that's expressed in UTC, known as the "epoch date", but that's an implementation detail. Imagine that when I capture a Date using the call Date(), I snap my fingers and the sound of the finger snap is heard all around the world at the same instant, without even speed-of-light delay. The finger snap is the moment that the call to Date() captures.
If I use the code let date = Date() I capture the current time all over the planet. A Date object doesn't have a specific time of day unless I convert it to a specific time zone. As others have suggested, the Calendar method dateComponents(in:from:) will let you extract components like the hour in a specific time zone from a Date, but you need to understand how all this works or you're going to get confused.
I suggest searching on the phrase "Calendrical Calculations" in the Xcode help system and reading that section of the docs. It's very helpful.
Im using this repo
https://github.com/remirobert/Tempo
Can someone help me understand how to grab the current time zone of the device, and then notify tempo? I am using the timeAgoNow() function of tempo to find display how long ago the post was made, but the timezone difference is messing it up. My datasource is using UTC time.
Cocoa uses UTC internally. for all of its date/time calculations.
If you create an NSDate for now:
NSDate()
You get a date that is the number of elapsed seconds since midnight, 1970 in UTC time.
Dates only have time zones when you display them.
By default logging a date to the console logs it in UTC, which can be confusing.
If I'm working on a project that does a lot of date/time calculations I'll create a debugging method that converts an NSDate to a date/time string in the current locale, which is easier to read/debug without having to mentally convert from UTC back to local time.
I have never used Tempo, so I don't know if it is using date strings, NSDate, or "internet dates" (which are also in UTC, but use a different "zero date" or "epoch date")