I'm using a UIDatePicker to select a time. I am also customising the background of the picker, however I need 2 different images depending on whether the user is using 12 hour mode (which displays AM/PM column) or 24 hour mode.
How can I detect the users setting for the 12/24 hour time?
Thanks
Even shorter than the others:
NSString *format = [NSDateFormatter dateFormatFromTemplate:#"j" options:0 locale:[NSLocale currentLocale]];
BOOL is24Hour = ([format rangeOfString:#"a"].location == NSNotFound);
Explanation
The string formatting character to represent the am/pm symbol is "a", as documented in Unicode Locale Markup Language – Part 4: Dates.
The same document also explains the special template symbol "j":
This is a special-purpose symbol. It must not occur in pattern or skeleton data. Instead, it is reserved for use in skeletons passed to APIs doing flexible date pattern generation. In such a context, it requests the preferred hour format for the locale (h, H, K, or k), as determined by whether h, H, K, or k is used in the standard short time format for the locale. In the implementation of such an API, 'j' must be replaced by h, H, K, or k before beginning a match against availableFormats data. Note that use of 'j' in a skeleton passed to an API is the only way to have a skeleton request a locale's preferred time cycle type (12-hour or 24-hour).
The NSString method dateFormatFromTemplate:options:locale: is described in Apple's NSDateFormatter documentation:
Returns a localized date format string representing the given date format components arranged appropriately for the specified locale.
So, what that method will do is turn the #"j" you pass in as a template in to a format string suitable for NSDateFormatter. If this string contains the am / pm symbol #"a" in it anywhere, then you know the locale (and other user settings being interrogated by the OS for you) wants am / pm to be displayed.
Swift (3.x) version of two most popular solutions in form of Date extension :
extension Date {
static var is24HoursFormat_1 : Bool {
let dateString = Date.localFormatter.string(from: Date())
if dateString.contains(Date.localFormatter.amSymbol) || dateString.contains(Date.localFormatter.pmSymbol) {
return false
}
return true
}
static var is24HoursFormat_2 : Bool {
let format = DateFormatter.dateFormat(fromTemplate: "j", options: 0, locale: Locale.autoupdatingCurrent)
return !format!.contains("a")
}
private static let localFormatter : DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale.autoupdatingCurrent
formatter.timeStyle = .short
formatter.dateStyle = .none
return formatter
}()
}
Usage :
Date.is24HoursFormat_1
Date.is24HoursFormat_2
Swift (2.0) version of two most popular solutions in form of NSDate extension :
extension NSDate {
class var is24HoursFormat_1 : Bool {
let dateString = NSDate.localFormatter.stringFromDate(NSDate())
if dateString.containsString(NSDate.localFormatter.AMSymbol) || dateString.containsString(NSDate.localFormatter.PMSymbol) {
return false
}
return true
}
class var is24HoursFormat_2 : Bool {
let format = NSDateFormatter.dateFormatFromTemplate("j", options: 0, locale: NSLocale.autoupdatingCurrentLocale())
return !format!.containsString("a")
}
private static let localFormatter : NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.locale = NSLocale.autoupdatingCurrentLocale()
formatter.timeStyle = .ShortStyle
formatter.dateStyle = .NoStyle
return formatter
}()
}
Please note that Apple says following on NSDateFormatter (Date Formatters) :
Creating a date formatter is not a cheap operation. If you are likely
to use a formatter frequently, it is typically more efficient to cache
a single instance than to create and dispose of multiple instances.
One approach is to use a static variable.
Thats the reason for static let
Secondly you should use NSLocale.autoupdatingCurrentLocale() ( for is24HoursFormat_1 ), that way you will always get the actual current state.
one
two
three
and there's probably many, many more...
Related
We are developing a mobile application using Xamarin cross platform development for Android and iOS, which is reading data from a Bluetooth device that transfers data as a float i.e. 12.22 and it will support multiple languages.
When it's set to English all our calculations are fine, but when set to non-english the calculations go completely wrong. I know what is wrong in non-english languages it's treating the "." as an "," which is means a 12.22 becomes 12,22. Therefore, screwing up our calculations.
I have found similar problems, but they force the complete language to English which means we lose the translations and other fixes look very complex for what must be a simple fix.
Is there a easy fix to this problem, like setting a flag in the Xamarin multi-language support
Update :
With the BLE transfer code, we can see the string but when it convert it we get 6000000 for French or 60 for English.
private async Task<double>
GetDatastoreValue(Plugin.BLE.Abstractions.Contracts.IService service, string param)
{
try
{
var datastore_param_char = await service.GetCharacteristicAsync(Guid.Parse(datastore_param_uid));
var datastore_param_val = await service.GetCharacteristicAsync(Guid.Parse(datastore_value_uid));
await datastore_param_char.WriteAsync(Encoding.ASCII.GetBytes(param));
byte[] result = await datastore_param_val.ReadAsync();
string rslt = Encoding.ASCII.GetString(result);
Debug.WriteLine("GetDatastoreValue -> result " + result);
double val = Convert.ToDouble(rslt);
Debug.WriteLine("GetDatastoreValue -> val -> "+ val);
return val;
}
catch
{
return 0.0;
}
}
instead of using this
double val = Convert.ToDouble(rslt);
do this
double val = Double.Parse(rslt, CultureInfo.InvariantCulture);
this will force it to evaluate "123.45" using the decimal separator, regardless of what the user's local culture might be set to
I am given LocalDateTime object created from String. I want to check whether that original string has "seconds" parameter or not.
My two inputs are:
String a = "2016-06-22T10:01"; //not given
String b = "2016-06-22T10:01:00"; //not given
LocalDateTime dateA = LocalDateTime.parse(a, DateTimeFormatter.ISO_DATE_TIME);
LocalDateTime dateB = LocalDateTime.parse(b, DateTimeFormatter.ISO_DATE_TIME);
Problem is I am given dateA and dateB, not a and b.
I have tried various ways like converting LocalDateTime to String and finding its length. For that I used two approaches.
date.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME).length();
date.toString().length();
But first approach gives length 19 for both dateA and dateB whereas second approach gives length 16 for both dateA and dateB.
I am unable to find any way to differentiate dateA and dateB.
As other people have already said, a LocalDateTime-object has always a second part. The other question is if the original input has got a second part or not. It is possible to find out the answer with only Java-8-means (but it is ugly because it is based on exception control flow):
String a = "2016-06-22T10:01"; // not given
String b = "2016-06-22T10:01:00"; // given
boolean hasSecondPart;
try {
TemporalAccessor tacc =
DateTimeFormatter.ISO_DATE_TIME.parseUnresolved(a, new ParsePosition(0));
tacc.get(ChronoField.SECOND_OF_MINUTE);
hasSecondPart = true;
} catch (UnsupportedTemporalTypeException ex) {
hasSecondPart = false;
}
System.out.println(hasSecondPart); // true for input b, false for input a
Side note:
An exception-free check if the string input has a second part or not would be possible with my library Time4J using this code:
boolean hasSecondPart =
Iso8601Format.EXTENDED_DATE_TIME.parseRaw(a).contains(PlainTime.SECOND_OF_MINUTE);
In ISO_DATE_TIME, seconds are optional (and set to zero if not present), that's why it parses both inputs. And LocalDateTime.toString() method will print the seconds only if it's not zero.
So, once you have the LocalDateTime objects created, you can't know if the original String had the seconds field.
To validate if the seconds field is present in the input String, you must create your own pattern and check if it throws exception when parsing:
// formatter with required seconds
DateTimeFormatter withSecs = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
LocalDateTime.parse(b, withSecs); // OK
LocalDateTime.parse(a, withSecs); // DateTimeParseException
If you want just to check if the field is present, but don't want to build a LocalDateTime object, you can also use parseUnresolved method, which doesn't throw exceptions:
ParsePosition position = new ParsePosition(0);
withSecs.parseUnresolved(a, position);
if(position.getErrorIndex() == -1) {
System.out.println("No error (it contains seconds)"); // b gets here
} else {
System.out.println("Error (it does not contain seconds)"); // a gets here
}
In Java 8 DateTime API
dates can be represented in the following way:
LocalDate as year-month-day
LocalDateTime as
year-month-day-hour-minute-second
ZonedDateTime as
year-month-day-hour-minute-second with time zone
As you can see, there is no way to differentiate between year-month-day-hour-minute-second and year-month-day-hour-minute. So, after the conversion from String to LocalDateTime is done - you can't differentiate it. The only way to do this is with String (either by length or regex), not with LocalDateTime object.
I'm having trouble converting optional input String to Int in order to do calculations on it.
let odoField = UITextField() // allows entry of text to iOS field
odoField.text = "12500" // simulated input
let odoString = odoField.text
// now here is where I get trouble...
if let odoInt = odoString.toInt() {
distance = Double(odoInt)
}
Apparently the toInt suffix is no longer part of Swift. I have tried the following:
if let odoInt = Int(odoString)() {
But then I get the error "Optional type String? is not unwrapped" and a suggestion to put a ! or ?, as in:
if let odoInt = Int(odoString!)() {
But then I STILL get the euro about unwrapping, with the suggestion that I add yet another !, then when I do that, another error that I get rid of the parens, like this:
if let odoInt = Int(odoString!)! {
And then I get ANOTHER error that "Initializer for conditional binding must have Optional type, not 'Int'."
I'm trying to create conditional unwrapping, here.
Help!
First thing to understand is that UITextField.text returns an optional string, so in your code, odoString is of type String?. Also, keep in mind that the Int constructor takes a String, not a String? so you have to unwrap the String? before you can use it. Just putting a ! after the variable (as in Int(odoString!)) will crash your app if the odoString is nil. Better would be something like this:
if let s = odoString, odoInt = Int(s) {
// odoInt is of type Int. It is guaranteed to have a value in this block
}
I've tested Daniel T's answer and it worked.
I have a situation where I want to get the result of a text field back as an optional Int. You can extend this to cover your case using the following code:
let odoInt = odoField.text != nil ? Int(odoField.text!) : nil
if let odoInt = odoInt {
// Use unwrapped odoInt here
}
Another option - for a more compact solution - is to use a flatMap:
let number = odoString.flatMap { Double($0) } ?? 0.0
In fact, it appears that the answer in Swift 2 (Xcode 7 beta 6) is simpler than anything above. The code does not choke on a nil value for odoString when I do e.g. the following:
if let odoInt = Int(odoString!) {
distance = Double(odoInt)
}
I therefore surmise, barring deeper knowledge to the contrary, that the compiler does treat this as "if the statement is True (the right side is valid), then define and initialize the variable, and continue with execution." I welcome further feedback. This does render unnecessary a lot of the extra code that is suggested above.
Consider the following class, which can be executed in the Xcode Playground:
import Foundation
class MyClass : NSObject {
var stringProperty:String
var integerProperty:Int
var linkedInstance:MyClass!
init(String string:String, Int int:Int) {
stringProperty = string
integerProperty = int
}
}
The following lines work as expected.
var myInstance = MyClass(String: "xxx", Int: 32)
myInstance.valueForKey("stringProperty")
myInstance.setValue(2, forKey: "integerProperty")
myInstance.valueForKey("integerProperty")
var anotherInstance = MyClass(String: "yyy", Int: 33)
myInstance.linkedInstance = anotherInstance
myInstance.linkedInstance.integerProperty = 2
However trying to access the value using KVC via a compound path,
myInstance.valueForKey("linkedInstance.integerProperty")
causess the error "Execution was interrupted, reason signal SIGABRT." I have not made any compound paths work with Swift, yet they work fine in Objective-C. Dose this have to do with optionals? Values for simple paths are returned wrapped.
As soon as you change that last line to
myInstance.valueForKeyPath("linkedInstance.integerProperty")
your Playground will play a lot nicer with you.
The lesson is: Do not confuse valueForKey: with valueForKeyPath:! The expression valueForKey("linkedInstance.integerProperty") can never succeed in Swift or in Objective-C, because"linkedInstance.integerProperty" is not a key - it is a key path.
Here's my test code:
var myDict: [String: AnyObject] = ["k":"v"]
var a = myDict["k"]
var b = a as String
var c = myDict["k"] as String
Here's my Swift playground in Xcode6-beta6:
According to the rules of type inference, doesn't complaining about c logically contradict not-complaining about b?
I believe that this is a bug. Part of what is going on here is that String is not an object. If you change the first line to:
var myDict: [String: Any] = ["k":"v"]
then everything is fine. So, given that string is not an object, casting a variable of type AnyObject? to a String should definitely yield an error. And, since the compiler has already decided that a is of type AnyObject? it should complain about casting a to a String.
Note that if you change the last line to:
var c = myDict["k"] as NSString
the error goes away supporting the notion that the issue is that String is not an object. You get the same complaint if you put an Int as the value in the array and try to cast that to an Int.
Update:
So the plot thickens. If you don't import Foundation or import something that imports Foundation, then you get additional errors. Without Foundation:
So clearly some of this has to do with the dual nature of Strings as non-objects and NSStrings as objects and the ability to use Strings as NSStrings when Foundation is imported.
This has to do with the fact that Dictionary has two subscript overloads:
subscript (key: Key) -> Value?
subscript (i: DictionaryIndex<Key, Value>) -> (Key, Value) { get }
The first is the familiar one where you pass a key and it gives you an optional of the value; and you can use to set the value on a key.
The second one is less common. I believe DictionaryIndex is a kind of iterator into the dictionary, and you can use it as a subscript to directly get the key-value pair at that iterator.
When the compiler can't find an overload that matches (in this case, the first one doesn't match because it returns an optional, which cannot be cast to non-optional String), it just picks one arbitrarily (well, it seems arbitrary to me anyway) to show in the error. In this place, it picks the second one, which you don't recognize. That's why the error seems weird to you.
This works.
var c = myDict["k"] as AnyObject! as String // "v"
To answer your question, the reason Swift complains could be that you are trying to do these two conversions in one go. Remember, the statement var a = myDict["k"] contains an implicit conversion already. The implied conversion is AnyObject?, so the above would also work like this:
var c = myDict["k"] as AnyObject? as String // "v"
Note that the above would lead to a run time error if the key "k" where not defined. You would allow this to return nil by casting to String?.