iOS - 3-letter Timezone abbreviation for NSTimeZone - ios

How can I get three letter abbreviations in iOS for a TimeZone
[NSTimeZone abbreviationDictionary] gives abbreviation as 3-letter code. For eg:
NSZT, PDT, EST etc.
However,
NSString * ss = [NSTimeZone timeZoneWithName:#"Pacific/Auckland"].abbreviation;
gives GMT+12.
Is there a way I can get NSZT/NZDT instead?

You can get a full standard localized name of timezone using below code
Objective c:
NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:#"Pacific/Auckland"];
NSString* timeZoneName = [timeZone localizedName:NSTimeZoneNameStyleStandard
locale:[NSLocale currentLocale]];
NSLog(#"%#", timeZoneName);
Swift:
let timezone:TimeZone = TimeZone.init(identifier: "Pacific/Auckland") ?? TimeZone.current
print(timezone.localizedName(for: .generic, locale: .autoupdatingCurrent))
print(timezone.localizedName(for: .standard, locale: .autoupdatingCurrent))
Output:
Objective c:
New Zealand Standard Time
Swift:
Optional("New Zealand Standard Time")
Optional("New Zealand Standard Time")
I think now you can get NZST from New Zealand Standard Time by splitting and combining string
Objective c:
NSMutableString * firstCharacters = [NSMutableString string];
NSArray *wordsArray = [timeZoneName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
for (NSString * word in wordsArray){
if ([word length] > 0){
NSString * firstLetter = [word substringToIndex:1];
[firstCharacters appendString:[firstLetter uppercaseString]];
}
}
NSLog(#"%#", firstCharacters);
Swift:
let fullName = timezone.localizedName(for: .standard, locale: .autoupdatingCurrent) ?? ""
var result = ""
fullName.enumerateSubstrings(in: fullName.startIndex..<fullName.endIndex, options: .byWords) { (substring, _, _, _) in
if let substring = substring { result += substring.prefix(1) }
}
print(result)
Output:
NZST

As mentioned in the comment by rmaddy abbreviationDictionary only has 51 entries, but if you use only names from the abbreviationDictionary you can use following code:
NSDictionary *dict = [NSTimeZone abbreviationDictionary];
NSArray *abbreviations = [dict allKeysForObject:#"Pacific/Auckland"];
if (abbreviations.count > 0) {
NSLog(#"%#", abbreviations.firstObject);
}

Related

Find nearest date in string array

So, I've got an sorted NSArray that contains NSString object (downloaded from a server), with the format: yyyy-MM-dd.
It's pretty much like this:
NSArray <NSString *> *dates = #[#"2017-06-25",
#"2017-06-26",
#"2017-06-27",
#"2017-06-28",
#"2017-06-30",
#"2017-07-01",
#"2017-07-02",
#"2017-07-03"];
So, today is 2017-06-29, and it's not in the array. How do I get the next nearest one? In this sample is 06-30, but it might be 07-01 if 06-30 doesn't exist...
Update
So people are asking me about what I've attempted to do. So it's like this (not very effective, but work)
Find if today is in the array (if yes, return)
Loop dates:
2.1 Convert dateString to date
2.2 Compare if date is greater than today => return if YES
If not found in step#2, return last object in dates array.
Actual code:
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.dateFormat = #"yyyy-MM-dd";
NSDate *today = [NSDate date];
NSUInteger index = [dates indexOfObject:[formatter stringFromDate:today]];
// Step 1
if (index == NSNotFound) {
// Step 2: Loop converted
NSInteger i = 0;
for (NSString *date in dates) {
// Step2.1: find the next nearest date's index
NSDate *convertedDate = [formmater dateFromString:date];
// Step2.2: Compare
if ([convertedDate intervalSinceDate:today] > 0) {
index = i;
break;
}
i++;
}
// Step 3: Still not found, index = last index
if (index == NSNotFound) index = i-1;
}
return dates[index];
This doesn't look so good because I might reload the dates array pretty much. Can I have a better solution?
Your algorithm is not bad, though your code doesn't appear to implement it (no sort?). If you'd like to improve it consider this:
First there is probably little point in doing a first scan to check for an exact match - that is potentially a linear search (implemented by indexOfObject:) through an unordered array, and if it fails you have to scan again for a close match, just do them at the same time.
Second there is no advantage in sorting, which is at best O(NlogN), as a linear search, O(N), will find you the answer you need.
Here is a sketch:
Convert the date you are searching for from NSString to NSDate, call it, say, target
Set bestMatch, an NSString to nil. Set bestDelta, an NSTimeInterval, to the maximum possible value DBL_MAX.
Iterate over your dates array:
3.1. Convert the string date to an NSDate, say date
3.2. Set delta to the difference between date and target
3.3. If delta is zero you have an exact match, return it
3.4. If delta is better than bestDelta, update bestDelta and bestMatch
After iteration bestMatch is the best match or nil if there wasn't one.
That is a single iteration, O(N), early return on exact match.
HTH
Please find the simplest solution for your problem. Updated solution based on sorting order!
We can use NSPredicate Block to solve.
static NSDateFormatter* formatter = nil;
static NSDate* today = nil;
// return an NSDate for a string given in yyyy-MM-dd
- (NSDate *)dateFromString:(NSString *)string {
if (formatter == nil) {
formatter = [NSDateFormatter new];
formatter.dateFormat = #"yyyy-MM-dd";
}
return [formatter dateFromString:string];
}
// Helps to return today date.
-(NSDate*) getTodayDate {
if (today == nil) {
today = [NSDate date];
}
return today;
}
// Helps to find nearest date from Array using Predicate
-(NSString*)findNearestDate:(NSArray*)dateArray {
today = nil;
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *dateString, NSDictionary *bind){
// this is the important part, lets get things in NSDate form so we can use them.
NSDate *dob = [self dateFromString:dateString];
NSComparisonResult result = [[self getTodayDate] compare:dob];
if (result == NSOrderedSame || result == NSOrderedAscending) {
return true;
}
return false;
}];
// Apply the predicate block.
NSArray *futureDates = [dateArray filteredArrayUsingPredicate:predicate];
if ([futureDates count] > 0) {
// Sort the Array.
futureDates = [futureDates sortedArrayUsingSelector: #selector(compare:)];
return [futureDates objectAtIndex:0];
}
return nil;
}
NSArray <NSString *> *dates = #[#"2017-06-25",
#"2017-06-26",
#"2017-06-27",
#"2017-06-28",
#"2017-06-30",
#"2017-07-01",
#"2017-07-02",
#"2017-07-03"];
NSLog(#"Nearest Date: %#", [self findNearestDate:dates]);
Answer: Nearest Date: 2017-06-30
1. Input
So you have an array of NSString like this
// input
NSArray<NSString *> * words = #[#"2017-06-25",
#"2017-06-26",
#"2017-06-27",
#"2017-06-28",
#"2017-06-30",
#"2017-07-01",
#"2017-07-02",
#"2017-07-03"];
2. Converting the array of NSString into an array of NSDate
First of all you need to convert the each input string into an NSDate
NSMutableArray<NSDate *> * dates = [NSMutableArray new];
NSDateFormatter * dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = #"yyyy-MM-dd";
for (NSString * word in words) {
[dates addObject:[dateFormatter dateFromString:word]];
}
3. Finding the nearestDate
Now you can find the nearest date
NSDate * nearestDate = nil;
NSTimeInterval deltaForNearesttDate = 0;
NSDate * now = [NSDate new];
for (NSDate * date in dates) {
NSTimeInterval delta = fabs([date timeIntervalSinceDate:now]);
if (nearestDate == nil || (delta < deltaForNearesttDate)) {
deltaForNearesttDate = delta;
nearestDate = date;
}
}
4. Conclusion
The result is into the nearestDate variable so
NSLog(#"%#", nearestDate);
Wed Jun 28 00:00:00 2017

Convert Datetime c# to Objective c and invertion

How to convert Datetime timestamp to a NSDate?
How to make the inverse?
My method to convert datetime to a string :
+(NSString*) dateTojson:(NSDate*)date{
return [NSString stringWithFormat:#"/Date(%f)/",(double)([date dateWithTimeIntervalSince1970] * 1000)];
}
My inverse method:
+(NSDate*) jsonToDate:(NSString *)json
{
double milisec = 0;
json = [[[json stringByReplacingOccurrencesOfString:#"/Date(" withString:#""] stringByReplacingOccurrencesOfString:#"/" withString:#""] stringByReplacingOccurrencesOfString:#"-0200" withString:#""];
NSArray *arr = [json componentsSeparatedByString:#"-"];
for(NSString *s in arr) {
if(![s isEqualToString:#""]){
milisec += [s doubleValue];
}
}
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(milisec / 1000.0)];
return date;
}
When i use [self jsonToDate:#"/Date(1495497600)/"] where 1495497600 represents "05/23/2017", the method return me a wrong date (result = "01/18/1970").
Why?
Notes:
i'm not considering the time, only date.
My variable milisec is equals to 1495497600, so i think the problem is the method dateWithTimeIntervalSince1970.
already try some posts like:
Convert milliseconds to NSDate
How to Convert a milliseconds to nsdate in objective C
You don't really need to divide the milliseconds at the end:
NSDate *date = [NSDate dateWithTimeIntervalSince1970:milisec];
Result:
2017-05-23 00:00:00 +0000

Formatting a month string is not working

I'm trying to create month strings that look like "Jan", "Feb", "Mar"... Here is my code:
- (NSString *)getMonthNameString:(int)monthNumber {
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"MMM"];
NSArray *monthNames = [formatter standaloneMonthSymbols];
NSString *monthName;
if (monthNumber > 0) {
return monthNames[monthNumber - 1];
}
return monthNames[1];
}
So if the month number is 1, I'm expecting the code to provide month name as "Jan" and if it is 2, it has to provide month name as "Feb" and so on. But the problem is that even though I have set the format as MMM, it is still creating month names of type "January", "February" etc instead of "Jan","Feb" etc. How do I sort this out?
Try:
-(NSString*)getMonthNameString:(int)monthNumber
{
NSDateFormatter *formate = [NSDateFormatter new];
[formate setDateFormat:#"MMM"];
NSArray *monthNames = [formate shortMonthSymbols];
NSString *monthName;
if (monthNumber > 0)
{
monthName = [monthNames objectAtIndex:(monthNumber - 1)];
}
return monthName;
}
That's usually not what a NSDateFormatter is for - it is for converting real dates, and not just month numbers.
If you want to stick with it, I suggest
-(NSString*)getMonthNameString:(int)monthNumber {
NSDateFormatter *formatter = [NSDateFormatter new];
return formatter.shortMonthSymbols[monthNumber-1];
}
I see no benefit in the extra check for > 0. This just masks programming errors. You might want to add an assertion to catch that during development. (Why should an invalid number return January anyway?)
Creating formatters is expensive, though - you might want to reuse the same instance over and over again.
Or just access an array directly, i.e.
-(NSString*)getMonthNameString:(int)monthNumber {
return #[#"Jan", #"Feb", ...][monthNumber-1]; // write up to December of course
}
standaloneMonthSymbols is a property of NSDateFormatter. I don't think it uses the dateFormat you've set. Try using shortStandaloneMonthSymbols property instead (or veryShortStandaloneMonthSymbols if you just need one letter symbol).
tahavath is right. Specifically, you want to use the shortStandaloneMonthSymbols property to get it to print "Jan" or "Feb" etc.
Try the following:
-(NSString*)getMonthNameString:(int)monthNumber
{
NSDateFormatter *formate = [NSDateFormatter new];
[formate setDateStyle:NSDateFormatterMediumStyle];
[formate setDateFormat:#"MMM"];
NSArray *monthNames = [formate standaloneMonthSymbols];
NSString *monthName;
if (monthNumber > 0)
{
monthName = [monthNames objectAtIndex:(monthNumber - 1)];
}
return monthName;
}
Check your code #Karuppu MGR
-(NSString*)getMonthNameString:(int)monthNumber
{
NSDateFormatter *formate = [NSDateFormatter new];
[formate setDateFormat:#"MMM"];
NSArray *monthNames = [formate standaloneMonthSymbols];
NSString *monthName;
if (monthNumber > 0 && monthNumber<13)
{
monthName = [monthNames objectAtIndex:(monthNumber - 1)];
// your process is right but , here you have attached "return value " so every time return the monthNames array value.
}
return monthName; // if you pass zero or greathan twelve monthName return nil value
}

Time Format so there's only 4 digits with a ":" in the middle

I want the concatenated NSString I have to be output in the format "00:00", the 0s being the digits in the concatenated string. And if there are not enough characters in the NSString, the other digits are made to be 0.
And if there are more than 4 digits than I want to only have the furthest right digits.
I have done this in Java before, I am assuming it's possible in Objective-C as well.
UIButton *button = sender;
NSString *concatenated = [self.input stringByAppendingString: button.titleLabel.text];
self.input = concatenated;
self.userOutput.text = self.input;
For example, I might get "89" as my concatenated string. I then want, self.input = 00:89.
OR
if I get 89374374 from my concatenated string, I then want self.input = 43:74.
I hope I am being clear
The following method should give the desired output:
- (NSString *)getFormattedTimeStringFromString:(NSString *)string
{
int input = [string intValue];
int mins = input % 100;
input /= 100;
int hours = input % 100;
return [NSString stringWithFormat:#"%02d:%02d", hours, mins];
}
You can use this by calling
self.input = [self getFormattedTimeStringFromString:concatenated];
Like this:
NSDateFormatter * df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"HH:mm"];
NSString *dateTimeStr = [df stringFromDate:[NSDate date]];
if ([concatenated length] == 2) {
self.input = [NSString stringWithFormat:#"00:%#",concatenated];
}
else
{
NSString *test = [concatenated substringFromIndex:[concatenated length] -4];
self.input = [NSString stringWithFormat:#"%#:%#",[test substringToIndex:2],[test substringFromIndex:[test length]-2]];
}
Please try above code it will fail if [concatenated length] is 3 or 1 , modify it accordingly

Format a UITextField for currency

I have a UITextField on my aplication that receives only numeric input from the user. This numeric input represents currency and have the default value of 0.00.
I would like to create something like a mask to format the UITextField as the user enter the numbers. For example:
9 becomes $0,09
99 becomes $0,99
999 becomes $999,99
The code below works great, but as I'm using integer and float values the app will eventually display wrong values afeter a certain point. For example:
999999999 becomes 100000000
That happens because flot and integer aren't precise enough and NSDEcimalNumber should be used. The point is that I can't figure out how to replace my integers and float values to NSDecimalNumber ones.
Does anyone could give me a hand to solve this? I spend some time searching the web for a solution but didn't find that suits my needs and tons of people with the same problem.
Heres the code:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.tag == 1){
NSString *cleanCentString = [[textField.text componentsSeparatedByCharactersInSet: [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:#""];
NSInteger centValue= cleanCentString.integerValue;
if (string.length > 0)
{
centValue = centValue * 10 + string.integerValue;
}
else
{
centValue = centValue / 10;
}
NSNumber *formatedValue;
formatedValue = [[NSNumber alloc] initWithFloat:(float)centValue / 100.0f];
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
textField.text = [_currencyFormatter stringFromNumber:formatedValue];
return NO;
}
if (textField.tag == 2){
// Nothing for now
}
return YES;
}
Implement UITextFieldDelegate and add next methods:
Swift:
let currencySign = "$"
// Adds $ before the text, e.g. "1" -> "$1" and allows "." and ","
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
var value = textField.text
var newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
var components = newString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890,.").invertedSet)
var decimalString = "".join(components) as NSString
var length = decimalString.length
if length > 0 {
value = "\(currencySign)\(decimalString)"
}
else {
value = ""
}
textField.text = value
}
// Formats final value using current locale, e.g. "130,50" -> "$130", "120.70" -> "$120.70", "5" -> "$5.00"
func textFieldDidEndEditing(textField: UITextField) {
var value = textField.text
var components = value.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890,.").invertedSet)
var decimalString = "".join(components) as NSString
let number = NSDecimalNumber(string: decimalString, locale:NSLocale.currentLocale())
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.locale = NSLocale.currentLocale()
if let formatedValue = formatter.stringFromNumber(number) {
textField.text = formatedValue
}
else {
textField.text = ""
}
}
You can convert integer or double/float value to string by following:
NSString *str = [#(myInt) stringValue];
NSString *str1 = [[NSNumber numberWithFloat:myFloat] stringValue];
After that you can convert string to NSDecimalNumber by many ways. Like:
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:#"100.1"];
NSLog(#"%#", number);
NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithString:#"100.1" locale:NSLocale.currentLocale];
NSLog(#"%#",num);
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setLocale:[NSLocale currentLocale]];
NSLog(#"%#", [formatter stringFromNumber:number]);
NSNumberFormatter will help you with the right formatting. Please read apples page for NSDecimalNumber and NSNumberFormatter.
NSNumberFormatter contains a section named"Configuring the Format of Currency". I didn't try it, but it seems something that can help you.
Let me know if this helps.. :)

Resources