I am trying to retrieve the timezone abbreviations of the local time zone by using the following code.
private func getLocalTimezone() -> String {
guard let localTimezone = TimeZone.current.abbreviation() else {
return ""
}
return localTimezone
}
But when I am in Indian time zone I am always getting GMT+5:30 where I need it as IST. Its coming correctly when I am in CST or PST. Can anyone please suggest a way to reach to the solution for this issue.
Thanks a lot in advance
This is because time zone abbreviations are locale sensitive. IST only means India Standard Time (Asia/Kolkata) in India. In other parts of the world, it could mean Israel Standard Time, or Irish Standard/Summer Time. Here's a site that shows you the list of abbreviations. You can see for yourself how ambiguous they can be.
This is why abbreviation() takes into account the region of your phone, i.e. this setting:
abbreviation() will give you "IST" if your device's region is India. If your phone is somewhere else, it shows "GMT+5:30" because that is the safest, most unambiguous option.
If you want it to output IST no matter where your device is, you need to hard code this by creating a dictionary of time zone identifiers to abbreviations that you want. There is a built in abbreviationDictionary that goes the other way - abbreviations to identifiers. You can search it, and it will work for IST (Asia/Kolkata), but might not work for whatever other time zone that you are interested in.
let abbreviation = TimeZone.abbreviationDictionary
.first(where: { $1 == TimeZone.current.identifier })?.key
and I'm not sure whether the contents of this dictionary will stay the same in future versions of iOS. Use it at your own risk.
Related
I currently have the following use case:
User receives a date in UTC from the backend
This date is transformed into local time for displaying purposes
The date is displayed in different inputs. One input for date and other for time
User can select time independently
The date should be sent back to the backend in UTC format as well
I'm not very experienced with time zones and I'm getting beaten by trying to allow the user to set up only the time (or date) in a datetime field.
My pseudo code is the following:
When receiving the from backend simply convert the date to show it to the user, making the orignal date stay in UTC
When the user picks the hour and minute (all in one action) use setHours and setMinutes from date-fns library
Use native toISOString() to set in models
Code so far [playground]:
import { utcToZonedTime, format } from "date-fns-tz";
import { setHours, setMinutes } from "date-fns";
const UTCStringdate = "2022-04-06T10:00:00.000000Z";
const userTimezone = "Asia/Bangkok";
const localizedTime = utcToZonedTime(UTCStringdate, userTimezone);
// Prints the correct information
// 10:00 in UTC is equal to 17:00 in Bangkok
console.log(format(localizedTime, "HH:mm"));
// Now I expext to set only the `minutes` and `hours`
// to this `localizedTime` so that I can update the backend
const [hours, minutes] = "10:30".split(":");
// Somewhere over here the `setHours` and `setMinutes`
// is turning the Date object into my own timezone
// and not using `Asia/Bangkok` timezone anymore
let newTime = setHours(localizedTime, hours);
newTime = setMinutes(newTime, minutes);
// Now I expect to print 17:30 since we only
// set up 30 minutes forward than the original one
// but it ends up printing 10:30
console.log(format(newTime, 'HH:mm'));
I understand that somewhere along the way (most likely in setHours and setMinutes) the date-fns library turns back the localizedTime back into my own timezone, completely ruining the idea of turning the Asia/Bangkok time into UTC.
Questions
First, is this the best approach to manipulate only the time part of a date when considering timezones? If not, anyone can point me to articles? I wasn't able to find anything on the topic
Second, how can I use setHours and setMinutes and still maintain the timezone?
There are no multiple time zones in JavaScript. There is UTC and there is your local one. What date-fns-tz does, is adjusting the time to the chosen user time zone. In your example, you can see this when printing both the original and the localized time
const utcTime = new Date(UTCStringdate);
console.log(utcTime.toISOString()); // -> 2022-04-06T10:00:00.000Z
console.log(localizedTime.toISOString()); // -> 2022-04-06T14:00:00.000Z
To solve your issue, convert UTC time to users time and let the user to adjust hours and minutes in his local time zone. After that, convert the time object back to UTC using zonedTimeToUtc
newTime = zonedTimeToUtc(newTime, userTimezone);
and then use newTime.toISOString() to send it back to the server.
console.log(newTime.toISOString());
EDIT: thanks to Alexander - Reinstate Monica. See the link in the comments for more info.
I ran into something odd today. I'm using firebase storage for an app and I want to compare the date of the file on the server with the one stored locally which I have manually set the attributes for when creating the file. If the dates are different then the app will re-load the file from the server like so:
if localModDate != serverModDate {
//re-download file
}
most of the time this works correctly, but sometimes the if statement get triggered even though the dates match (checked via print statements) and if I convert the stamps to strings then the dates always match correctly:
if String(describing: localModDate) != String(describing: serverModDate) {
//re-download file
}
my guess is that swift is converting to epoch time (I tested this and the epoch times are indeed off in like the 6th decimal place). Is this correct or is there something else going on here?
Date is just a TimeInterval (which is a typealias for Double) of elapsed seconds since the "reference date" (Jan 1 2001). The == operator just compares those two Doubles, so it's subject to the same floating point precision issues of regular Doubles.
Use Calendar.isDate(_:equalTo:toGranularity:) to check if the dates are "close enough":
if Calendar.current.isDate(localModDate, equalTo: serverModDate, toGranularity: .second) {
...
}
I want to display the following string on my time axis:
"GMT/BST"
Here's the code:
NSDateFormatter *dateformatter=[[NSDateFormatter alloc] init];
[dateformatter setDateFormat:#"zzz"];
timeZoneString = [NSMutableString stringWithFormat:#"%# / %#",[dateformatter stringFromDate:startDate],[dateformatter stringFromDate:endDate]];
But this gives "GMT/GMT+01:00"
What is the NSDateFormatter code to turn "GMT+01:00" into "BST" ? I can't get the right formatters to do this, having tried z|zzz|Z|ZZZ|v|V see... http://waracle.net/iphone-nsdateformatter-date-formatting-table/
Turns out there is a built in array of 48 time zone abbreviations (e.g. 'BST') in iOS.
NSDictionary *tzDict = [NSTimeZone abbreviationDictionary];
There is an array of 419 time zone names in this array (e.g. 'Europe/London'):
NSArray *timeZoneNames = [NSTimeZone knownTimeZoneNames];
tzDict contains the abbreviations for daylight saving time for a subset of time zone names. So the algorithm would be to check if we are in DST, then see if tzDict has an entry, and subsitute that or if not, use
[NSTimeZone abbreviation];
Here are a few other topics on time zones in general.
Daylight saving time and time zone best practices
How can I map tz database names to city and country names?
C# british summer time (BST) timezone abbreviation
http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
GMT timezone conversion in objective c
I spent a day on this and wanted to make sure so others don't get hung up on it.
For a UK region/locale, the 'z' field pattern in DateFormatter returns the proper string for the "America/London" timezone (e.g. "GMT" or "BST"). However, if you use the US region/locale, 'z' will give you "GMT+1".
The gotcha happens when an abbreviation for the given target timezone with a given locale doesn't exist. I was using the 'v' field pattern, and with the US region, "Europe/London" was falling back to "United Kingdom Time", which blew up my text label. 'z' is better, it falls back to "GMT+1" while in DST, but still not the desired "BST".
The abbreviationDictionary doesn't consider daylight savings time. It's meant to map an abbreviation to a timezone. You'll find "BST":"Americal/London", but this only applies while observing daylight savings time as mentioned by #Nick T. During standard time, Brits are used to seeing "GMT".
Unfortunately, without recreating timezone functionality yourself, you're left with compromises. I think iOS can do better here.
Reference
Date Field Symbol Table as part of the Unicode Technical Standard
My question is how can I get the same day, month, year, hour, minutes, seconds in a different time zone, for example:
var now = moment().valueOf();
var result1 = moment(now).format('DD-MM-YYYY HH:mm:SS Z');
In my time zone I get some this like this:
18-02-2015 21:08:34 +01:00
So how can I change only time zone without changing other values (days, months, ..., minutes, ...)
I want to get some thing like this:
result2: 18-02-2015 21:08:34 +01:00
result3: 18-02-2015 21:08:34 +10:00
result4: 18-02-2015 21:08:34 +05:00
result5: 18-02-2015 21:08:34 -06:00
result6: 18-02-2015 21:08:34 -11:00
Thanks in advance
Here's how you could do what you are asking:
// get a moment representing the current time
var now = moment();
// create a new moment based on the original one
var another = now.clone();
// change the offset of the new moment - passing true to keep the local time
another.utcOffset('+05:30', true);
// log the output
console.log(now.format()); // "2016-01-15T11:58:07-08:00"
console.log(another.format()); // "2016-01-15T11:58:07+05:30"
However, you must recognize two important things:
The another object no longer represents the current time - even in the target time zone. It's a completely different moment in time. (The world does not synchronize local clocks. If it did, we'd have no need for time zones!).
For this reason, even though the above code satisfies the question that was asked, I strongly recommend against using it. Instead, re-evaluate your requirements, as it's likely they are misunderstanding the nature of time and time zones.
A time zone cannot be fully represented by an offset alone. Read "Time Zone != Offset" in the timezone tag wiki. While some time zones have fixed offsets (such as +05:30 used by India), many time zones change their offsets at different points throughout the year to accommodate daylight saving time.
If you wanted to account for this, you could use moment-timezone instead of calling utcOffset(...). However, the issue in my first bullet would still apply.
// get a moment representing the current time
var now = moment();
// create a new moment based on the original one
var another = now.clone();
// change the time zone of the new moment - passing true to keep the local time
another.tz('America/New_York', true); // or whatever time zone you desire
// log the output
console.log(now.format()); // "2016-01-15T11:58:07-08:00"
console.log(another.format()); // "2016-01-15T11:58:07-05:00"
The most-voted answer is messy IMO. Here's a cleaner solution - similar to BlueSam's answer, but safer:
const myTime = moment.tz('2016-08-30T22:00:00', moment.ISO_8601, 'America/Denver')
myTime.format() //2016-08-30T22:00:00-06:00
const sameTimeDifferentZone = moment.tz(myTime.format('YYYY-MM-DDTHH:mm:ss.SSS'), moment.ISO_8601, 'America/New_York')
sameTimeDifferentZone.format() //2016-08-30T22:00:00-04:00
After reading the above comments, I thought I'd add in based on Joao's answer.
In my case I was trying to use a preexisting moment date with a timezone and converting it to another timezone while retaining the original date value (as asked in the question).
var newTimezone = 'America/Denver';
//date - contains existing moment with timezone i.e 'America/New_York'
moment.tz(date.format('YYYY-MM-DDTHH:mm:ss'), 'YYYY-MM-DDTHH:mm:ss', newTimezone);
From the moment docs: http://momentjs.com/timezone/docs/
reference moment-timezone-with-data.js and specify which timezone to go to, like so:
moment(date).tz("America/Los_Angeles").format()
Due to the limitation of date parsing on BlackBerry I'm trying to roll my own parse/deparse methods, however I seem to be falling foul of an hour's difference somewhere, somehow.
I do this:
long nowLong = System.currentTimeMillis();
String nowString = DateParser.longToString(nowLong);
Date nowDateFromString = DateParser.stringToDate(nowString);
Date nowDateFromLong = DateParser.longToDate(nowLong);
When outputted in order it produces this in console:
[139.46] 1369132556831
[139.46] 21 May 2013 11:35:56 Europe/Dublin
[139.46] Tue May 21 12:35:56 Europe/Dublin 2013
[139.46] Tue May 21 11:35:56 Europe/Dublin 2013
My simulator's time is set to 11:35 so the third statement - DateParser.stringToDate() - seems to be failing somewhere.
Here is my implementation:
public static Date stringToDate(String date) {
long l = HttpDateParser.parse(date);
Date d = new Date(l);
return d;
}
As my nowString includes the time zone I'd expect HttpDateParser.parse() to take this in to account but it seems not to be.
How can I correct this?
HttpDateParser.parse() is documented to handle "GMT" or a "TZD" which I assume to be a "time zone designator". I suspect this is expected to be the (horrible, ambiguous) abbreviation format - so for example, it might be worth trying to parse
21 May 2013 11:35:56 BST
and seeing what you get out. That would at least take you further in terms of diagnosing the behaviour of HttpDateParser. Keeping the time zone's TZDB ID is a better idea in my view, but you may well need to write your own parsing code. You still need to handle local time ambiguity though, where a particular local time occurs twice due to DST transitions.
It's not entirely clear what the input or expected output are in your case - how much control you have over the format. I'd try to use ISO-8601 as far as possible, with a time zone identifer as well if you need one. (If you're only trying to represent an instant in time, I'd use an ISO-8601 representation of the UTC instant, complete with a Z suffix to indicate UTC.)