What is the best way to deal with the NSDateFormatter locale “feechur”?

后端 未结 4 1938
花落未央
花落未央 2020-11-21 05:38

It seems that NSDateFormatter has a \"feature\" that bites you unexpectedly: If you do a simple \"fixed\" format operation such as:

NSDateForma         


        
相关标签:
4条回答
  • 2020-11-21 06:14

    Instead of subclassing, you could create an NSDateFormatter category with an additional initializer that takes care of assigning the locale and possibly also a format string, so you'd have a ready-to-use formatter right after initializing it.

    @interface NSDateFormatter (LocaleAdditions)
    
    - (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString;
    
    @end
    
    @implementation NSDateFormatter (LocaleAdditions)
    
    - (id)initWithPOSIXLocaleAndFormat:(NSString *)formatString {
        self = [super init];
        if (self) {
            NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
            [self setLocale:locale];
            [locale release];
            [self setFormat:formatString];
        }
        return self;
    }
    
    @end
    

    Then you could use NSDateFormatter anywhere in your code with just:

    NSDateFormatter* fmt = [[NSDateFormatter alloc] initWithPOSIXLocaleAndFormat:@"yyyyMMddHHmmss"];
    

    You might want to prefix your category method somehow to avoid name conflicts, just in case Apple decides to add such a method in a future version of the OS.

    In case you're always using the same date format(s), you could also add category methods that return singleton instances with certain configurations (something like +sharedRFC3339DateFormatter). Be aware however that NSDateFormatter is not thread-safe and you have to use locks or @synchronized blocks when you're using the same instance from multiple threads.

    0 讨论(0)
  • 2020-11-21 06:17

    Here is the solution for that problem in the swift version. In swift we can use extension instead of category. So, Here I have created the extension for the DateFormatter and inside that initWithSafeLocale returns the DateFormatter with the relevant Locale, Here in our case that is en_US_POSIX, Apart from that also provided couple of date formation methods.

    • Swift 4

      extension DateFormatter {
      
      private static var dateFormatter = DateFormatter()
      
      class func initWithSafeLocale(withDateFormat dateFormat: String? = nil) -> DateFormatter {
      
          dateFormatter = DateFormatter()
      
          var en_US_POSIX: Locale? = nil;
      
          if (en_US_POSIX == nil) {
              en_US_POSIX = Locale.init(identifier: "en_US_POSIX")
          }
          dateFormatter.locale = en_US_POSIX
      
          if dateFormat != nil, let format = dateFormat {
              dateFormatter.dateFormat = format
          }else{
              dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
          }
          return dateFormatter
      }
      
      // ------------------------------------------------------------------------------------------
      
      class func getDateFromString(string: String, fromFormat dateFormat: String? = nil) -> Date? {
      
          if dateFormat != nil, let format = dateFormat {
              dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
          }else{
              dateFormatter = DateFormatter.initWithSafeLocale()
          }
          guard let date = dateFormatter.date(from: string) else {
              return nil
          }
          return date
      }
      
      // ------------------------------------------------------------------------------------------
      
      class func getStringFromDate(date: Date, fromDateFormat dateFormat: String? = nil)-> String {
      
          if dateFormat != nil, let format = dateFormat {
              dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: format)
          }else{
              dateFormatter = DateFormatter.initWithSafeLocale()
          }
      
          let string = dateFormatter.string(from: date)
      
          return string
      }   }
      
    • usage description:

      let date = DateFormatter.getDateFromString(string: "11-07-2001”, fromFormat: "dd-MM-yyyy")
      print("custom date : \(date)")
      let dateFormatter = DateFormatter.initWithSafeLocale(withDateFormat: "yyyy-MM-dd HH:mm:ss")
      let dt = DateFormatter.getDateFromString(string: "2001-05-05 12:34:56")
      print("base date = \(dt)")
      dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
      let dateString = dateFormatter.string(from: Date())
      print("dateString = " + dateString)
      let date1 = dateFormatter.date(from: "2001-05-05 12:34:56")
      print("date1 = \(String(describing: date1))")
      let date2 = dateFormatter.date(from: "2001-05-05 22:34:56")
      print("date2 = \(String(describing: date2))")
      let date3 = dateFormatter.date(from: "2001-05-05 12:34:56PM")
      print("date3 = \(String(describing: date3))")
      let date4 = dateFormatter.date(from: "2001-05-05 12:34:56 PM")
      print("date4 = \(String(describing: date4))")
      
    0 讨论(0)
  • 2020-11-21 06:28

    May I suggest something totally different because to be honest all of this is somewhat running down a rabbit hole.

    You should be using one NSDateFormatter with dateFormat set and locale forced to en_US_POSIX for receiving dates (from servers/APIs).

    Then you should be using a different NSDateFormatter for the UI which you will set the timeStyle/dateStyle properties - this way you're not having an explicit dateFormat set by yourself, thus falsely assuming that format will be used.

    This means UI is driven by user preferences (am/pm vs 24 hour, and date strings formatted correctly to user choice - from iOS settings), whereas dates that are "coming into" your app are always being "parsed" correctly to an NSDate for you to use.

    0 讨论(0)
  • 2020-11-21 06:33

    Duh!!

    Sometimes you have an "Aha!!" moment, sometimes it's more of a "Duh!!" This is the latter. In the category for initWithSafeLocale the "super" init was coded as self = [super init];. This inits the SUPERCLASS of NSDateFormatter but does not init the NSDateFormatter object itself.

    Apparently when this initialization is skipped, setLocale "bounces off", presumably because of some missing data structure in the object. Changing the init to self = [self init]; causes the NSDateFormatter initialization to occur, and setLocale is happy again.

    Here is the "final" source for the category's .m:

    #import "NSDateFormatter+Locale.h"
    
    @implementation NSDateFormatter (Locale)
    
    - (id)initWithSafeLocale {
        static NSLocale* en_US_POSIX = nil;
        self = [self init];
        if (en_US_POSIX == nil) {
            en_US_POSIX = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
        }
        [self setLocale:en_US_POSIX];
        return self;    
    }
    
    @end
    
    0 讨论(0)
提交回复
热议问题