Why Nsdateformatter Is Returning Null for a 19/10/2014 in a Brazilian Time Zone

Why NSDateFormatter is returning null for a 19/10/2014 in a Brazilian time zone?

We can reproduce your problem by explicitly setting the time zone to “Brazil/East”:

#import 

int main(int argc, const char * argv[])
{

@autoreleasepool {
NSString *dateString = @"19/10/2014";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"Brazil/East"];
[dateFormatter setDateFormat:@"dd/MM/yyyy"];
NSDate *myDate = [dateFormatter dateFromString:dateString];
NSLog(@"myDate = %@", myDate);
}
return 0;
}

Here's the output:

2014-06-06 14:22:28.254 commandLine[31169:303] myDate = (null)

Since you didn't give a time in your dateString, the system assumes midnight. But midnight on that date doesn't exist in the Brazilian time zone.

Brazil changes from BRT (daylight-saving time zone) to BRST (non-daylight-saving time zone) on October 19, 2014, skipping directly from the last moment of “18/10/2014” to “19/10/2014 01:00:00”.

Since “19/10/2014 00:00:00” doesn't exist, NSDateFormatter returns nil. I think this is bad behavior on the part of NSDateFormatter, but we have to deal with it. -[NSDateFormatter dateFromString:] eventually calls CFDateFormatterGetAbsoluteTimeFromString, which uses the udat_parseCalendar function from the International Components for Unicode (icu) library to parse the date.

You can work around the problem by making the parser use noon instead of midnight as the default time. No time zones change to/from daylight saving time at noon. Let's write a helper function that returns noon of some arbitrary date in a given time zone:

static NSDate *someDateWithNoonWithTimeZone(NSTimeZone *timeZone) {
NSDateComponents *components = [[NSDateComponents alloc] init];
components.timeZone = timeZone;
components.era = 1;
components.year = 2001;
components.month = 1;
components.day = 1;
components.hour = 12;
components.minute = 0;
components.second = 0;
return [[NSCalendar autoupdatingCurrentCalendar] dateFromComponents:components];
}

Then we set the date formatter's defaultDate to this noon date:

int main(int argc, const char * argv[])
{

@autoreleasepool {
NSString *dateString = @"19/10/2014";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"Brazil/East"];
dateFormatter.dateFormat = @"dd/MM/yyyy";
dateFormatter.defaultDate = someDateWithNoonWithTimeZone(dateFormatter.timeZone);
NSDate *myDate = [dateFormatter dateFromString:dateString];
NSLog(@"myDate = %@", myDate);
}
return 0;
}

And here's the output:

2014-06-06 14:52:31.939 commandLine[31982:303] myDate = 2014-10-19 14:00:00 +0000

NSDateFormatter's dateFromString returning null with date from stringFromDate

This answer and @DarkDust's comment pointed me in the right direction.

It was indeed a problem with the Daylight Saving Time occuring at midnight in Brazil. So between 17 and 18 October, there was no midnight. So because I had the date format set to y-M-d, dateFromString: was trying to return a date at the local midnight, which does not exist, hence it returned nil.

Steps to reproduce:

dateFormatter.timeZone = [NSTimeZone timeZoneWithName:@"America/Sao_Paulo"];
NSLog([dateFormatter dateFromString:@"2015-10-18"]); // returns nil

I still have to find out a solution that would return me a date like 2015-10-18 01:00:00 for this time zone.

Why does NSDateFormatter return nil date for these 4 time zones?

I have a suspicion. Only a suspicion, but a pretty strong one.

That value represents October 19th 2064. The Brazilian time zones observe daylight saving time starting at local midnight - that's when their clocks go forward, so midnight itself doesn't exist. October 19th is one of those transitions.

Here's some sample code using Noda Time, my .NET date/time API. It checks whether the start of the day in every time zone it knows about is actually midnight:

using System;
using NodaTime;

class Test
{
static void Main()
{
var localDate = new LocalDate(2064, 10, 19);
var provider = DateTimeZoneProviders.Tzdb;
foreach (var id in provider.Ids)
{
var zone = provider[id];
var startOfDay = zone.AtStartOfDay(localDate).LocalDateTime.TimeOfDay;
if (startOfDay != LocalTime.Midnight)
{
Console.WriteLine(id);
}
}
}
}

That produces a very similar list:

America/Bahia
America/Campo_Grande
America/Cuiaba
America/Sao_Paulo
Brazil/East

I suspect Brazil/East may be an alias for America/Sao_Paolo, which is why it's not on your list.

Anyway, to get back to your Julian day issue - I suspect the formatter always wants to return an NSDate * which is at the local midnight. That doesn't exist for October 19th 2064 in those time zones... hence it returns nil. Personally I'd suggest it should return the 1am value instead, but hey...

1st april dates of 80s failed to parse in iOS 10.0

This problem occurs if daylight saving time starts exactly on
midnight, as it was the case in Moscow in the years 1981–1984 (see for example Clock Changes in Moscow, Russia (Moskva)).

This was also observed in

  • Why does NSDateFormatter return nil date for these 4 time zones? and
  • Why NSDateFormatter is returning null for a 19/10/2014 in a Brazilian time zone?

For example, at midnight of April 1st 1984, the clocks were adjusted one hour forward, which means that the date "1984-04-01 00:00"
does not exist in that timezone:

let dFmt = DateFormatter()
dFmt.dateFormat = "yyyy-MM-dd"
dFmt.timeZone = TimeZone(identifier: "Europe/Moscow")
print(dFmt.date(from: "1984-04-01")) // nil

As a solution, you can tell the date formatter to be "lenient":

dFmt.isLenient = true

and then it will return the first valid date on that day:

dFmt.isLenient = true
if let date = dFmt.date(from: "1984-04-01") {
dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dFmt.string(from: date))
}
// 1984-04-01 01:00:00

A different solution
was given by rob mayoff, which is to make the date formatter use noon instead of midnight as the
default date. Here is a translation of rob's code from Objective-C to Swift:

let noon = DateComponents(calendar: dFmt.calendar, timeZone: dFmt.timeZone,
year: 2001, month: 1, day: 1, hour: 12, minute: 0, second: 0)
dFmt.defaultDate = noon.date
if let date = dFmt.date(from: "1984-04-01") {
dFmt.dateFormat = "yyyy-MM-dd HH:mm:ss"
print(dFmt.string(from: date))
}
// 1984-04-01 12:00:00

Swift Dateformatting fails for just a specific day

I suppose you are living in Brasil.

On October 16, 2016 the daylight saving time changes and there is no 0:00.

Why create Date from String only failed On Real iphone?

this link may solve your problem.....

Swift

formatter.locale = Locale(identifier: "en_US")

Swift 5 - Unexplainable DateFormatter Crash

In your locale the daylight saving time changes at 2 am on March 14, 2021, therefore the particular date doesn't exist.

Given an NSDate, find the last day of fourth prior month

As you've already discovered, you need a starting date and a calendar:

NSDate *startingDate = [NSDate date];
NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];

You'll need the components of the current date but only down to the current month, because you don't care about the specific day within the month:

NSDateComponents *components = [calendar
components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth
fromDate:startingDate];

You say you want the last day of the fourth prior month. Since months have different numbers of days, the last day varies depending on the month. But all months have first days, and those first days are always numbered 1. So it's easiest to compute “the last day of the fourth prior month” by first going back three months:

components.month -= 3;

Then, go one day prior to that month:

components.day = -1;

Finally, you need to get clear in your head that an NSDate represents an instant in time, but a day (like “April 1st, 2015”) is an interval of time, starting and ending at specific instants. If you're going to represent a whole day using an NSDate, you're going to be storing one instant within that interval. You don't want to store the first or last instant (which will both be midnights); that causes problems for some days in some time zones. Instead, use noon as your instant:

components.hour = 12;

Now you're ready to ask the calendar for a new NSDate:

NSDate *lastDayOfFourthPriorMonth = [calendar dateFromComponents:components];


Related Topics



Leave a reply



Submit