title: Losing double precision when dealing with timestamps
body: <p>I have a simple <code>XCTestCase</code>:</p>
  
  <pre><code>func testExample() {
      let date = "2015-09-21T20:38:54.379912Z";// as NSString;
      let date1 = 1442867934.379912;
  
      XCTAssertEqual(date1, NSDate.sam_dateFromISO8601String(date).timeIntervalSince1970);
  }
  </code></pre>
  
  <p>This test passes, when it shouldn't, for two reasons:</p>
  
  <ul>
  <li><code>1442867934.379912</code> becomes <code>1442867934.379911</code> in the test, even though its literally just a variable re-printed</li>
  <li>the <code>sam_</code> function (copied below) seems to think the same way, the <code>millisecond</code> variable becomes <code>millisecond  double  0.37991200000000003 0.37991200000000003</code> which when later converted from <code>NSDate</code> to <code>double</code>, seems to lose the microsecond precision (debugger):
  <code>po NSDate.sam_dateFromISO8601String(date).timeIntervalSince1970  -&gt; 1442867934.37991</code> </li>
  </ul>
  
  <p><code>po 1442867934.379912 -&gt; 1442867934.37991</code></p>
  
  <p>Any idea why? The microsecond precision (6 decimal places) is really important to my application, I need to seamlessly convert to and from <code>NSString</code> and <code>NSDate</code> using the <code>iso8601</code> format.</p>
  
  <hr>
  
  <pre><code>+ (NSDate *)sam_dateFromISO8601String:(NSString *)iso8601 {
      // Return nil if nil is given
      if (!iso8601 || [iso8601 isEqual:[NSNull null]]) {
          return nil;
      }
  
      // Parse number
      if ([iso8601 isKindOfClass:[NSNumber class]]) {
          return [NSDate dateWithTimeIntervalSince1970:[(NSNumber *)iso8601 doubleValue]];
      }
  
      // Parse string
      else if ([iso8601 isKindOfClass:[NSString class]]) {
          const char *str = [iso8601 cStringUsingEncoding:NSUTF8StringEncoding];
          size_t len = strlen(str);
          if (len == 0) {
              return nil;
          }
  
          struct tm tm;
          char newStr[25] = "";
          BOOL hasTimezone = NO;
  
          // 2014-03-30T09:13:00Z
          if (len == 20 &amp;&amp; str[len - 1] == 'Z') {
              strncpy(newStr, str, len - 1);
          }
  
          // 2014-03-30T09:13:00-07:00
          else if (len == 25 &amp;&amp; str[22] == ':') {
              strncpy(newStr, str, 19);
              hasTimezone = YES;
          }
  
          // 2014-03-30T09:13:00.000Z
          else if (len == 24 &amp;&amp; str[len - 1] == 'Z') {
              strncpy(newStr, str, 19);
          }
          // 2014-03-30T09:13:00.000000Z
          else if (len == 27 &amp;&amp; str[len - 1] == 'Z') {
              strncpy(newStr, str, 19);
          }
  
  
          // 2014-03-30T09:13:00.000-07:00
          else if (len == 29 &amp;&amp; str[26] == ':') {
              strncpy(newStr, str, 19);
              hasTimezone = YES;
          }
  
          // Poorly formatted timezone
          else {
              strncpy(newStr, str, len &gt; 24 ? 24 : len);
          }
  
          // Timezone
          size_t l = strlen(newStr);
          if (hasTimezone) {
              strncpy(newStr + l, str + len - 6, 3);
              strncpy(newStr + l + 3, str + len - 2, 2);
          } else {
              strncpy(newStr + l, "+0000", 5);
          }
  
          // Add null terminator
          newStr[sizeof(newStr) - 1] = 0;
  
          if (strptime(newStr, "%FT%T%z", &amp;tm) == NULL) {
              return nil;
          }
  
          double millisecond = 0.0f;
  
          NSString *subStr = [[iso8601 componentsSeparatedByString:@"."].lastObject substringToIndex:6];
  
          millisecond = subStr.doubleValue/1000000.f;
  
          time_t t;
          t = mktime(&amp;tm);
  
          return [NSDate dateWithTimeIntervalSince1970:t + millisecond];
      }
  
      NSAssert1(NO, @"Failed to parse date: %@", iso8601);
      return nil;
  }
  </code></pre>