Bug 525741 - two JAR tests fail if modification date of their files is on day daylight saving time starts or ends and after time change. r=dtownsend,tglek
authorMichal Novotny <michal.novotny@gmail.com>
Wed, 16 Dec 2009 00:01:08 +0100
changeset 37369 cc65a0921a1b9849cb805f987fa5243005a4e2be
parent 37368 c922c6402f7631b27cabb2ba769da5c880c1a43a
child 37370 7e68181ddedbc2b2fc6d11f3aeab1e57e55d6f90
push id11260
push userdgottwald@mozilla.com
push dateThu, 21 Jan 2010 13:30:20 +0000
treeherdermozilla-central@cc65a0921a1b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdtownsend, tglek
bugs525741
milestone1.9.3a1pre
Bug 525741 - two JAR tests fail if modification date of their files is on day daylight saving time starts or ends and after time change. r=dtownsend,tglek
modules/libjar/nsJAR.cpp
modules/libjar/nsJAR.h
modules/libjar/nsJARInputStream.cpp
modules/libjar/nsZipArchive.cpp
modules/libjar/nsZipArchive.h
modules/libjar/test/unit/test_bug379841.js
modules/libjar/zipstruct.h
modules/libjar/zipwriter/src/StreamFunctions.h
modules/libjar/zipwriter/src/nsZipHeader.cpp
modules/libjar/zipwriter/src/nsZipHeader.h
modules/libjar/zipwriter/src/nsZipWriter.cpp
modules/libjar/zipwriter/test/unit/head_zipwriter.js
modules/libjar/zipwriter/test/unit/test_asyncadd.js
modules/libjar/zipwriter/test/unit/test_bug419769_1.js
modules/libjar/zipwriter/test/unit/test_bug425768.js
modules/libjar/zipwriter/test/unit/test_deflatedata.js
modules/libjar/zipwriter/test/unit/test_editexisting.js
modules/libjar/zipwriter/test/unit/test_storedata.js
modules/libjar/zipwriter/test/unit/test_sync.js
--- a/modules/libjar/nsJAR.cpp
+++ b/modules/libjar/nsJAR.cpp
@@ -254,24 +254,19 @@ nsJAR::Extract(const char *zipEntry, nsI
     nsCAutoString path;
     rv = outFile->GetNativePath(path);
     if (NS_FAILED(rv)) return rv;
 
     rv = mZip.ExtractFile(item, path.get(), fd);
   }
   if (NS_FAILED(rv)) return rv;
 
-  PRTime prtime = GetModTime(item->Date(), item->Time());
   // nsIFile needs milliseconds, while prtime is in microseconds.
-  PRTime conversion = LL_ZERO;
-  PRTime newTime = LL_ZERO;
-  LL_I2L(conversion, PR_USEC_PER_MSEC);
-  LL_DIV(newTime, prtime, conversion);
   // non-fatal if this fails, ignore errors
-  outFile->SetLastModifiedTime(newTime);
+  outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP    
 nsJAR::GetEntry(const char *aEntryName, nsIZipEntry* *result)
 {
   nsZipItem* zipItem = mZip.GetItem(aEntryName);
@@ -928,18 +923,17 @@ nsJAREnumerator::GetNext(nsACString& aRe
 
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARItem, nsIZipEntry)
 
 nsJARItem::nsJARItem(nsZipItem* aZipItem)
     : mSize(aZipItem->Size()),
       mRealsize(aZipItem->RealSize()),
       mCrc32(aZipItem->CRC32()),
-      mDate(aZipItem->Date()),
-      mTime(aZipItem->Time()),
+      mLastModTime(aZipItem->LastModTime()),
       mCompression(aZipItem->Compression()),
       mIsDirectory(aZipItem->IsDirectory()),
       mIsSynthetic(aZipItem->isSynthetic)
 {
 }
 
 //------------------------------------------
 // nsJARItem::GetCompression
@@ -1016,17 +1010,17 @@ nsJARItem::GetIsSynthetic(PRBool *aIsSyn
 //------------------------------------------
 // nsJARItem::GetLastModifiedTime
 //------------------------------------------
 NS_IMETHODIMP
 nsJARItem::GetLastModifiedTime(PRTime* aLastModTime)
 {
     NS_ENSURE_ARG_POINTER(aLastModTime);
 
-    *aLastModTime = GetModTime(mDate, mTime);
+    *aLastModTime = mLastModTime;
     return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsIZipReaderCache
 
 NS_IMPL_THREADSAFE_ISUPPORTS3(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
 
@@ -1262,32 +1256,9 @@ nsZipReaderCache::Observe(nsISupports *a
   }
   else if (strcmp(aTopic, "chrome-flush-caches") == 0) {
     mZips.Enumerate(DropZipReaderCache, nsnull);
     mZips.Reset();
   }
   return NS_OK;
 }
 
-PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
-{
-  PRExplodedTime time;
-
-  time.tm_usec = 0;
-  
-  time.tm_hour = (aTime >> 11) & 0x1F;
-  time.tm_min = (aTime >> 5) & 0x3F;
-  time.tm_sec = (aTime & 0x1F) * 2;
-
-  time.tm_year = (aDate >> 9) + 1980;
-  time.tm_month = ((aDate >> 5) & 0x0F)-1;
-  time.tm_mday = aDate & 0x1F;
-  
-  time.tm_params.tp_gmt_offset = 0;
-  time.tm_params.tp_dst_offset = 0;
-  
-  PR_NormalizeTime(&time, PR_GMTParameters);
-  time.tm_params = PR_LocalTimeParameters(&time);
-  
-  return PR_ImplodeTime(&time);
-}
-
 ////////////////////////////////////////////////////////////////////////////////
--- a/modules/libjar/nsJAR.h
+++ b/modules/libjar/nsJAR.h
@@ -80,18 +80,16 @@ typedef enum
   JAR_INVALID_SIG         = 2,
   JAR_INVALID_UNKNOWN_CA  = 3,
   JAR_INVALID_MANIFEST    = 4,
   JAR_INVALID_ENTRY       = 5,
   JAR_NO_MANIFEST         = 6,
   JAR_NOT_SIGNED          = 7
 } JARManifestStatusType;
 
-PRTime GetModTime(PRUint16 aDate, PRUint16 aTime);
-
 /*-------------------------------------------------------------------------
  * Class nsJAR declaration. 
  * nsJAR serves as an XPCOM wrapper for nsZipArchive with the addition of 
  * JAR manifest file parsing. 
  *------------------------------------------------------------------------*/
 class nsJAR : public nsIZipReader, public nsIJAR
 {
   // Allows nsJARInputStream to call the verification functions
@@ -179,18 +177,17 @@ public:
     
     nsJARItem(nsZipItem* aZipItem);
     virtual ~nsJARItem() {}
 
 private:
     PRUint32     mSize;             /* size in original file */
     PRUint32     mRealsize;         /* inflated size */
     PRUint32     mCrc32;
-    PRUint16     mDate;
-    PRUint16     mTime;
+    PRTime       mLastModTime;
     PRUint16     mCompression;
     PRPackedBool mIsDirectory; 
     PRPackedBool mIsSynthetic;
 };
 
 /**
  * nsJAREnumerator
  *
--- a/modules/libjar/nsJARInputStream.cpp
+++ b/modules/libjar/nsJARInputStream.cpp
@@ -341,17 +341,17 @@ nsJARInputStream::ReadDirectory(char* aB
 
             const char * entryName = mArray[mArrPos].get();
             PRUint32 entryNameLen = mArray[mArrPos].Length();
             nsZipItem* ze = mJar->mZip.GetItem(entryName);
             NS_ENSURE_TRUE(ze, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
 
             // Last Modified Time
             PRExplodedTime tm;
-            PR_ExplodeTime(GetModTime(ze->Date(), ze->Time()), PR_GMTParameters, &tm);
+            PR_ExplodeTime(ze->LastModTime(), PR_GMTParameters, &tm);
             char itemLastModTime[65];
             PR_FormatTimeUSEnglish(itemLastModTime,
                                    sizeof(itemLastModTime),
                                    " %a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ",
                                    &tm);
 
             // write a 201: line to the buffer for this item
             // 200: filename content-length last-modified file-type
--- a/modules/libjar/nsZipArchive.cpp
+++ b/modules/libjar/nsZipArchive.cpp
@@ -99,18 +99,18 @@ nsRecyclingAllocator *gZlibAllocator = N
 #endif  /* XP_UNIX */
 
 
 static const PRUint32 kMaxNameLength = PATH_MAX; /* Maximum name length */
 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
 static const PRUint16 kSyntheticTime = 0;
 static const PRUint16 kSyntheticDate = (1 + (1 << 5) + (0 << 9));
 
-static PRUint16 xtoint(const unsigned char *ii);
-static PRUint32 xtolong(const unsigned char *ll);
+static PRUint16 xtoint(const PRUint8 *ii);
+static PRUint32 xtolong(const PRUint8 *ll);
 static PRUint32 HashName(const char* aName, PRUint16 nameLen);
 #if defined(XP_UNIX) || defined(XP_BEOS)
 static nsresult ResolveSymlink(const char *path);
 #endif
 
 //***********************************************************
 // Allocators for use with zlib
 //
@@ -843,80 +843,153 @@ static PRUint32 HashName(const char* aNa
 }
 
 /*
  *  x t o i n t
  *
  *  Converts a two byte ugly endianed integer
  *  to our platform's integer.
  */
-static PRUint16 xtoint (const unsigned char *ii)
+static PRUint16 xtoint (const PRUint8 *ii)
 {
   return (PRUint16) ((ii [0]) | (ii [1] << 8));
 }
 
 /*
  *  x t o l o n g
  *
  *  Converts a four byte ugly endianed integer
  *  to our platform's integer.
  */
-static PRUint32 xtolong (const unsigned char *ll)
+static PRUint32 xtolong (const PRUint8 *ll)
 {
   return (PRUint32)( (ll [0] <<  0) |
                      (ll [1] <<  8) |
                      (ll [2] << 16) |
                      (ll [3] << 24) );
 }
 
-PRUint32 const nsZipItem::LocalOffset()
+/*
+ * GetModTime
+ *
+ * returns last modification time in microseconds
+ */
+static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
+{
+  // Note that on DST shift we can't handle correctly the hour that is valid
+  // in both DST zones
+  PRExplodedTime time;
+
+  time.tm_usec = 0;
+
+  time.tm_hour = (aTime >> 11) & 0x1F;
+  time.tm_min = (aTime >> 5) & 0x3F;
+  time.tm_sec = (aTime & 0x1F) * 2;
+
+  time.tm_year = (aDate >> 9) + 1980;
+  time.tm_month = ((aDate >> 5) & 0x0F) - 1;
+  time.tm_mday = aDate & 0x1F;
+
+  time.tm_params.tp_gmt_offset = 0;
+  time.tm_params.tp_dst_offset = 0;
+
+  PR_NormalizeTime(&time, PR_GMTParameters);
+  time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
+  PR_NormalizeTime(&time, PR_GMTParameters);
+  time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
+
+  return PR_ImplodeTime(&time);
+}
+
+PRUint32 nsZipItem::LocalOffset()
 {
   return xtolong(central->localhdr_offset);
 }
 
-PRUint32 const nsZipItem::Size()
+PRUint32 nsZipItem::Size()
 {
   return isSynthetic ? 0 : xtolong(central->size);
 }
 
-PRUint32 const nsZipItem::RealSize()
+PRUint32 nsZipItem::RealSize()
 {
   return isSynthetic ? 0 : xtolong(central->orglen);
 }
 
-PRUint32 const nsZipItem::CRC32()
+PRUint32 nsZipItem::CRC32()
 {
   return isSynthetic ? 0 : xtolong(central->crc32);
 }
 
-PRUint16 const nsZipItem::Date()
+PRUint16 nsZipItem::Date()
 {
   return isSynthetic ? kSyntheticDate : xtoint(central->date);
 }
 
-PRUint16 const nsZipItem::Time()
+PRUint16 nsZipItem::Time()
 {
   return isSynthetic ? kSyntheticTime : xtoint(central->time);
 }
 
-PRUint16 const nsZipItem::Compression()
+PRUint16 nsZipItem::Compression()
 {
   return isSynthetic ? STORED : xtoint(central->method);
 }
 
-bool const nsZipItem::IsDirectory()
+bool nsZipItem::IsDirectory()
 {
   return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
 }
 
-PRUint16 const nsZipItem::Mode()
+PRUint16 nsZipItem::Mode()
 {
   if (isSynthetic) return 0755;
   return ((PRUint16)(central->external_attributes[2]) | 0x100);
 }
 
+const PRUint8 * nsZipItem::GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize)
+{
+  if (isSynthetic) return NULL;
+
+  const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
+                             nameLength;
+  PRUint32 buflen = (PRUint32)xtoint(central->extrafield_len);
+  PRUint32 pos = 0;
+  PRUint16 tag, blocksize;
+
+  while (buf && (pos + 4) <= buflen) {
+    tag = xtoint(buf + pos);
+    blocksize = xtoint(buf + pos + 2);
+
+    if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
+      *aBlockSize = blocksize;
+      return buf + pos;
+    }
+
+    pos += blocksize + 4;
+  }
+
+  return NULL;
+}
+
+
+PRTime nsZipItem::LastModTime()
+{
+  if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
+
+  // Try to read timestamp from extra field
+  PRUint16 blocksize;
+  const PRUint8 *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
+  if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
+    return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
+  }
+
+  return GetModTime(Date(), Time());
+}
+
 #if defined(XP_UNIX) || defined(XP_BEOS)
-bool const nsZipItem::IsSymlink()
+bool nsZipItem::IsSymlink()
 {
   if (isSynthetic) return false;
   return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
 }
 #endif
+
--- a/modules/libjar/nsZipArchive.h
+++ b/modules/libjar/nsZipArchive.h
@@ -78,28 +78,30 @@ struct PRFileDesc;
  * each nsZipItem represents one file in the archive and all the
  * information needed to manipulate it.
  */
 class nsZipItem
 {
 public:
   const char* Name() { return ((const char*)central) + ZIPCENTRAL_SIZE; }
 
-  PRUint32 const LocalOffset();
-  PRUint32 const Size();
-  PRUint32 const RealSize();
-  PRUint32 const CRC32();
-  PRUint16 const Date();
-  PRUint16 const Time();
-  PRUint16 const Compression();
-  bool     const IsDirectory();
-  PRUint16 const Mode();
+  PRUint32 LocalOffset();
+  PRUint32 Size();
+  PRUint32 RealSize();
+  PRUint32 CRC32();
+  PRUint16 Date();
+  PRUint16 Time();
+  PRUint16 Compression();
+  bool     IsDirectory();
+  PRUint16 Mode();
+  const PRUint8* GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize);
+  PRTime   LastModTime();
 
 #if defined(XP_UNIX) || defined(XP_BEOS)
-  bool     const IsSymlink();
+  bool     IsSymlink();
 #endif
 
   nsZipItem*         next;
   const ZipCentral*  central;
   PRUint16           nameLength;
   bool               isSynthetic;
 };
 
--- a/modules/libjar/test/unit/test_bug379841.js
+++ b/modules/libjar/test/unit/test_bug379841.js
@@ -2,18 +2,18 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const path = "data/test_bug379841.zip";
 // Retrieved time should be within 2 seconds of original file's time.
 const MAX_TIME_DIFF = 2000000;
 
 var ENTRY_NAME = "test";
-// Actual time of file was 07 May 2007 14:35:49
-var ENTRY_TIME = new Date(2007, 4, 7, 14, 35, 49, 0);
+// Actual time of file was 07 May 2007 13:35:49 UTC
+var ENTRY_TIME = new Date(Date.UTC(2007, 4, 7, 13, 35, 49, 0));
 
 function run_test() {
   var file = do_get_file(path);
   var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
                   createInstance(Ci.nsIZipReader);
   zipReader.open(file);
   var entry = zipReader.getEntry(ENTRY_NAME);
   var diff = Math.abs(entry.lastModifiedTime - ENTRY_TIME.getTime()*1000);
--- a/modules/libjar/zipstruct.h
+++ b/modules/libjar/zipstruct.h
@@ -117,16 +117,20 @@ typedef struct ZipEnd_
  */ 
 #define ZIPEND_SIZE (4+2+2+2+2+4+4+2)
 
 /* signatures */
 #define LOCALSIG    0x04034B50l
 #define CENTRALSIG  0x02014B50l
 #define ENDSIG      0x06054B50l
 
+/* extra fields */
+#define EXTENDED_TIMESTAMP_FIELD   0x5455
+#define EXTENDED_TIMESTAMP_MODTIME 0x01
+
 /* compression methods */
 #define STORED            0
 #define SHRUNK            1
 #define REDUCED1          2
 #define REDUCED2          3
 #define REDUCED3          4
 #define REDUCED4          5
 #define IMPLODED          6
--- a/modules/libjar/zipwriter/src/StreamFunctions.h
+++ b/modules/libjar/zipwriter/src/StreamFunctions.h
@@ -45,57 +45,53 @@
 #include "nsIOutputStream.h"
 
 /*
  * ZIP file data is stored little-endian. These are helper functions to read and
  * write little endian data to/from a char buffer.
  * The off argument, where present, is incremented according to the number of
  * bytes consumed from the buffer.
  */
-inline NS_HIDDEN_(void) WRITE8(char* buf, PRUint32* off, PRUint8 val)
+inline NS_HIDDEN_(void) WRITE8(PRUint8* buf, PRUint32* off, PRUint8 val)
 {
-  buf[(*off)++] = val & 0xff;
+  buf[(*off)++] = val;
 }
 
-inline NS_HIDDEN_(void) WRITE16(char* buf, PRUint32* off, PRUint16 val)
+inline NS_HIDDEN_(void) WRITE16(PRUint8* buf, PRUint32* off, PRUint16 val)
 {
-  buf[(*off)++] = val & 0xff;
-  buf[(*off)++] = (val >> 8) & 0xff;
+  WRITE8(buf, off, val & 0xff);
+  WRITE8(buf, off, (val >> 8) & 0xff);
 }
 
-inline NS_HIDDEN_(void) WRITE32(char* buf, PRUint32* off, PRUint32 val)
+inline NS_HIDDEN_(void) WRITE32(PRUint8* buf, PRUint32* off, PRUint32 val)
 {
-  buf[(*off)++] = val & 0xff;
-  buf[(*off)++] = (val >> 8) & 0xff;
-  buf[(*off)++] = (val >> 16) & 0xff;
-  buf[(*off)++] = (val >> 24) & 0xff;
+  WRITE16(buf, off, val & 0xffff);
+  WRITE16(buf, off, (val >> 16) & 0xffff);
 }
 
-inline NS_HIDDEN_(PRUint8) READ8(char* buf, PRUint32* off)
+inline NS_HIDDEN_(PRUint8) READ8(const PRUint8* buf, PRUint32* off)
 {
-  return (PRUint8)buf[(*off)++];
+  return buf[(*off)++];
 }
 
-inline NS_HIDDEN_(PRUint16) READ16(char* buf, PRUint32* off)
+inline NS_HIDDEN_(PRUint16) READ16(const PRUint8* buf, PRUint32* off)
 {
-  PRUint16 val = (PRUint16)buf[(*off)++] & 0xff;
-  val |= ((PRUint16)buf[(*off)++] & 0xff) << 8;
+  PRUint16 val = READ8(buf, off);
+  val |= READ8(buf, off) << 8;
   return val;
 }
 
-inline NS_HIDDEN_(PRUint32) READ32(char* buf, PRUint32* off)
+inline NS_HIDDEN_(PRUint32) READ32(const PRUint8* buf, PRUint32* off)
 {
-  PRUint32 val = (PRUint32)buf[(*off)++] & 0xff;
-  val |= ((PRUint32)buf[(*off)++] & 0xff) << 8;
-  val |= ((PRUint32)buf[(*off)++] & 0xff) << 16;
-  val |= ((PRUint32)buf[(*off)++] & 0xff) << 24;
+  PRUint32 val = READ16(buf, off);
+  val |= READ16(buf, off) << 16;
   return val;
 }
 
-inline NS_HIDDEN_(PRUint32) PEEK32(unsigned char *buf)
+inline NS_HIDDEN_(PRUint32) PEEK32(const PRUint8* buf)
 {
   return (PRUint32)( (buf [0]      ) |
                      (buf [1] <<  8) |
                      (buf [2] << 16) |
                      (buf [3] << 24) );
 }
 
 NS_HIDDEN_(nsresult) ZW_ReadData(nsIInputStream *aStream, char *aBuffer, PRUint32 aCount);
--- a/modules/libjar/zipwriter/src/nsZipHeader.cpp
+++ b/modules/libjar/zipwriter/src/nsZipHeader.cpp
@@ -42,16 +42,19 @@
 
 #define ZIP_FILE_HEADER_SIGNATURE 0x04034b50
 #define ZIP_FILE_HEADER_SIZE 30
 #define ZIP_CDS_HEADER_SIGNATURE 0x02014b50
 #define ZIP_CDS_HEADER_SIZE 46
 
 #define FLAGS_IS_UTF8 0x800
 
+#define ZIP_EXTENDED_TIMESTAMP_FIELD 0x5455
+#define ZIP_EXTENDED_TIMESTAMP_MODTIME 0x01
+
 /**
  * nsZipHeader represents an entry from a zip file.
  */
 NS_IMPL_ISUPPORTS1(nsZipHeader, nsIZipEntry)
 
 /* readonly attribute unsigned short compression; */
 NS_IMETHODIMP nsZipHeader::GetCompression(PRUint16 *aCompression)
 {
@@ -100,33 +103,52 @@ NS_IMETHODIMP nsZipHeader::GetIsDirector
     return NS_OK;
 }
 
 /* readonly attribute PRTime lastModifiedTime; */
 NS_IMETHODIMP nsZipHeader::GetLastModifiedTime(PRTime *aLastModifiedTime)
 {
     NS_ASSERTION(mInited, "Not initalised");
 
+    // Try to read timestamp from extra field
+    PRUint16 blocksize;
+    const PRUint8 *tsField = GetExtraField(ZIP_EXTENDED_TIMESTAMP_FIELD, PR_FALSE, &blocksize);
+    if (tsField && blocksize >= 5) {
+        PRUint32 pos = 4;
+        PRUint8 flags;
+        flags = READ8(tsField, &pos);
+        if (flags & ZIP_EXTENDED_TIMESTAMP_MODTIME) {
+            *aLastModifiedTime = (PRTime)(READ32(tsField, &pos))
+                                 * PR_USEC_PER_SEC;
+            return NS_OK;
+        }
+    }
+
+    // Use DOS date/time fields
+    // Note that on DST shift we can't handle correctly the hour that is valid
+    // in both DST zones
     PRExplodedTime time;
 
     time.tm_usec = 0;
 
-    time.tm_hour = mTime >> 11;
+    time.tm_hour = (mTime >> 11) & 0x1F;
     time.tm_min = (mTime >> 5) & 0x3F;
     time.tm_sec = (mTime & 0x1F) * 2;
 
     time.tm_year = (mDate >> 9) + 1980;
     time.tm_month = ((mDate >> 5) & 0x0F) - 1;
     time.tm_mday = mDate & 0x1F;
 
     time.tm_params.tp_gmt_offset = 0;
     time.tm_params.tp_dst_offset = 0;
 
     PR_NormalizeTime(&time, PR_GMTParameters);
-    time.tm_params = PR_LocalTimeParameters(&time);
+    time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
+    PR_NormalizeTime(&time, PR_GMTParameters);
+    time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
 
     *aLastModifiedTime = PR_ImplodeTime(&time);
 
     return NS_OK;
 }
 
 /* readonly attribute boolean isSynthetic; */
 NS_IMETHODIMP nsZipHeader::GetIsSynthetic(PRBool *aIsSynthetic)
@@ -144,65 +166,95 @@ void nsZipHeader::Init(const nsACString 
 
     PRExplodedTime time;
     PR_ExplodeTime(aDate, PR_LocalTimeParameters, &time);
 
     mTime = time.tm_sec / 2 + (time.tm_min << 5) + (time.tm_hour << 11);
     mDate = time.tm_mday + ((time.tm_month + 1) << 5) +
             ((time.tm_year - 1980) << 9);
 
+    // Store modification timestamp as extra field
+    // First fill CDS extra field
+    mFieldLength = 9;
+    mExtraField = new PRUint8[mFieldLength];
+    if (!mExtraField) {
+        mFieldLength = 0;
+    } else {
+        PRUint32 pos = 0;
+        WRITE16(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_FIELD);
+        WRITE16(mExtraField.get(), &pos, 5);
+        WRITE8(mExtraField.get(), &pos, ZIP_EXTENDED_TIMESTAMP_MODTIME);
+        WRITE32(mExtraField.get(), &pos, aDate / PR_USEC_PER_SEC);
+
+        // Fill local extra field
+        mLocalExtraField = new PRUint8[mFieldLength];
+        if (mLocalExtraField) {
+            mLocalFieldLength = mFieldLength;
+            memcpy(mLocalExtraField.get(), mExtraField.get(), mLocalFieldLength);
+        }
+    }
+
     mEAttr = aAttr;
     mOffset = aOffset;
     mName = aPath;
     mComment = NS_LITERAL_CSTRING("");
     // Claim a UTF-8 path in case it needs it.
     mFlags |= FLAGS_IS_UTF8;
     mInited = PR_TRUE;
 }
 
 PRUint32 nsZipHeader::GetFileHeaderLength()
 {
-    return ZIP_FILE_HEADER_SIZE + mName.Length();
+    return ZIP_FILE_HEADER_SIZE + mName.Length() + mLocalFieldLength;
 }
 
 nsresult nsZipHeader::WriteFileHeader(nsIOutputStream *aStream)
 {
     NS_ASSERTION(mInited, "Not initalised");
 
-    char buf[ZIP_FILE_HEADER_SIZE];
+    PRUint8 buf[ZIP_FILE_HEADER_SIZE];
     PRUint32 pos = 0;
     WRITE32(buf, &pos, ZIP_FILE_HEADER_SIGNATURE);
     WRITE16(buf, &pos, mVersionNeeded);
     WRITE16(buf, &pos, mFlags);
     WRITE16(buf, &pos, mMethod);
     WRITE16(buf, &pos, mTime);
     WRITE16(buf, &pos, mDate);
     WRITE32(buf, &pos, mCRC);
     WRITE32(buf, &pos, mCSize);
     WRITE32(buf, &pos, mUSize);
     WRITE16(buf, &pos, mName.Length());
-    WRITE16(buf, &pos, 0);
+    WRITE16(buf, &pos, mLocalFieldLength);
 
-    nsresult rv = ZW_WriteData(aStream, buf, pos);
+    nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = ZW_WriteData(aStream, mName.get(), mName.Length());
     NS_ENSURE_SUCCESS(rv, rv);
 
-    return ZW_WriteData(aStream, mName.get(), mName.Length());
+    if (mLocalFieldLength)
+    {
+      rv = ZW_WriteData(aStream, (const char *)mLocalExtraField.get(), mLocalFieldLength);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    return NS_OK;
 }
 
 PRUint32 nsZipHeader::GetCDSHeaderLength()
 {
     return ZIP_CDS_HEADER_SIZE + mName.Length() + mComment.Length() +
            mFieldLength;
 }
 
 nsresult nsZipHeader::WriteCDSHeader(nsIOutputStream *aStream)
 {
     NS_ASSERTION(mInited, "Not initalised");
 
-    char buf[ZIP_CDS_HEADER_SIZE];
+    PRUint8 buf[ZIP_CDS_HEADER_SIZE];
     PRUint32 pos = 0;
     WRITE32(buf, &pos, ZIP_CDS_HEADER_SIGNATURE);
     WRITE16(buf, &pos, mVersionMade);
     WRITE16(buf, &pos, mVersionNeeded);
     WRITE16(buf, &pos, mFlags);
     WRITE16(buf, &pos, mMethod);
     WRITE16(buf, &pos, mTime);
     WRITE16(buf, &pos, mDate);
@@ -212,35 +264,35 @@ nsresult nsZipHeader::WriteCDSHeader(nsI
     WRITE16(buf, &pos, mName.Length());
     WRITE16(buf, &pos, mFieldLength);
     WRITE16(buf, &pos, mComment.Length());
     WRITE16(buf, &pos, mDisk);
     WRITE16(buf, &pos, mIAttr);
     WRITE32(buf, &pos, mEAttr);
     WRITE32(buf, &pos, mOffset);
 
-    nsresult rv = ZW_WriteData(aStream, buf, pos);
+    nsresult rv = ZW_WriteData(aStream, (const char *)buf, pos);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ZW_WriteData(aStream, mName.get(), mName.Length());
     NS_ENSURE_SUCCESS(rv, rv);
     if (mExtraField) {
-        rv = ZW_WriteData(aStream, mExtraField, mFieldLength);
+        rv = ZW_WriteData(aStream, (const char *)mExtraField.get(), mFieldLength);
         NS_ENSURE_SUCCESS(rv, rv);
     }
     return ZW_WriteData(aStream, mComment.get(), mComment.Length());
 }
 
 nsresult nsZipHeader::ReadCDSHeader(nsIInputStream *stream)
 {
     NS_ASSERTION(!mInited, "Already initalised");
 
-    char buf[ZIP_CDS_HEADER_SIZE];
+    PRUint8 buf[ZIP_CDS_HEADER_SIZE];
 
-    nsresult rv = ZW_ReadData(stream, buf, ZIP_CDS_HEADER_SIZE);
+    nsresult rv = ZW_ReadData(stream, (char *)buf, ZIP_CDS_HEADER_SIZE);
     NS_ENSURE_SUCCESS(rv, rv);
 
     PRUint32 pos = 0;
     PRUint32 signature = READ32(buf, &pos);
     if (signature != ZIP_CDS_HEADER_SIGNATURE)
         return NS_ERROR_FILE_CORRUPTED;
 
     mVersionMade = READ16(buf, &pos);
@@ -266,27 +318,49 @@ nsresult nsZipHeader::ReadCDSHeader(nsII
         rv = ZW_ReadData(stream, field.get(), namelength);
         NS_ENSURE_SUCCESS(rv, rv);
         mName.Assign(field, namelength);
     }
     else
         mName = NS_LITERAL_CSTRING("");
 
     if (mFieldLength > 0) {
-        mExtraField = new char[mFieldLength];
+        mExtraField = new PRUint8[mFieldLength];
         NS_ENSURE_TRUE(mExtraField, NS_ERROR_OUT_OF_MEMORY);
-        rv = ZW_ReadData(stream, mExtraField.get(), mFieldLength);
+        rv = ZW_ReadData(stream, (char *)mExtraField.get(), mFieldLength);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (commentlength > 0) {
         nsAutoArrayPtr<char> field(new char[commentlength]);
         NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
         rv = ZW_ReadData(stream, field.get(), commentlength);
         NS_ENSURE_SUCCESS(rv, rv);
         mComment.Assign(field, commentlength);
     }
     else
         mComment = NS_LITERAL_CSTRING("");
 
     mInited = PR_TRUE;
     return NS_OK;
 }
+
+const PRUint8 * nsZipHeader::GetExtraField(PRUint16 aTag, PRBool aLocal, PRUint16 *aBlockSize)
+{
+    const PRUint8 *buf = aLocal ? mLocalExtraField : mExtraField;
+    PRUint32 buflen = aLocal ? mLocalFieldLength : mFieldLength;
+    PRUint32 pos = 0;
+    PRUint16 tag, blocksize;
+
+    while (buf && (pos + 4) <= buflen) {
+      tag = READ16(buf, &pos);
+      blocksize = READ16(buf, &pos);
+
+      if (aTag == tag && (pos + blocksize) <= buflen) {
+        *aBlockSize = blocksize;
+        return buf + pos - 4;
+      }
+
+      pos += blocksize;
+    }
+
+    return NULL;
+}
--- a/modules/libjar/zipwriter/src/nsZipHeader.h
+++ b/modules/libjar/zipwriter/src/nsZipHeader.h
@@ -63,55 +63,61 @@ public:
 
     nsZipHeader() :
         mCRC(0),
         mCSize(0),
         mUSize(0),
         mEAttr(0),
         mOffset(0),
         mFieldLength(0),
+        mLocalFieldLength(0),
         mVersionMade(0x0300 + 23), // Generated on Unix by v2.3 (matches infozip)
         mVersionNeeded(20), // Requires v2.0 to extract
         mFlags(0),
         mMethod(0),
         mTime(0),
         mDate(0),
         mDisk(0),
         mIAttr(0),
         mInited(PR_FALSE),
-        mExtraField(NULL)
+        mExtraField(NULL),
+        mLocalExtraField(NULL)
     {
     }
 
     ~nsZipHeader()
     {
         mExtraField = NULL;
+        mLocalExtraField = NULL;
     }
 
     PRUint32 mCRC;
     PRUint32 mCSize;
     PRUint32 mUSize;
     PRUint32 mEAttr;
     PRUint32 mOffset;
     PRUint32 mFieldLength;
+    PRUint32 mLocalFieldLength;
     PRUint16 mVersionMade;
     PRUint16 mVersionNeeded;
     PRUint16 mFlags;
     PRUint16 mMethod;
     PRUint16 mTime;
     PRUint16 mDate;
     PRUint16 mDisk;
     PRUint16 mIAttr;
     PRPackedBool mInited;
     nsCString mName;
     nsCString mComment;
-    nsAutoArrayPtr<char> mExtraField;
+    nsAutoArrayPtr<PRUint8> mExtraField;
+    nsAutoArrayPtr<PRUint8> mLocalExtraField;
 
     void Init(const nsACString & aPath, PRTime aDate, PRUint32 aAttr,
               PRUint32 aOffset);
     PRUint32 GetFileHeaderLength();
     nsresult WriteFileHeader(nsIOutputStream *aStream);
     PRUint32 GetCDSHeaderLength();
     nsresult WriteCDSHeader(nsIOutputStream *aStream);
     nsresult ReadCDSHeader(nsIInputStream *aStream);
+    const PRUint8 * GetExtraField(PRUint16 aTag, PRBool aLocal, PRUint16 *aBlockSize);
 };
 
 #endif
--- a/modules/libjar/zipwriter/src/nsZipWriter.cpp
+++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp
@@ -142,60 +142,60 @@ nsresult nsZipWriter::ReadFile(nsIFile *
     // If the file is too short, it cannot be a valid archive, thus we fail
     // without even attempting to open it
     NS_ENSURE_TRUE(size > ZIP_EOCDR_HEADER_SIZE, NS_ERROR_FILE_CORRUPTED);
 
     nsCOMPtr<nsIInputStream> inputStream;
     rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    char buf[1024];
+    PRUint8 buf[1024];
     PRInt64 seek = size - 1024;
     PRUint32 length = 1024;
 
     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(inputStream);
 
     while (true) {
         if (seek < 0) {
             length += (PRInt32)seek;
             seek = 0;
         }
 
         rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, seek);
         if (NS_FAILED(rv)) {
             inputStream->Close();
             return rv;
         }
-        rv = ZW_ReadData(inputStream, buf, length);
+        rv = ZW_ReadData(inputStream, (char *)buf, length);
         if (NS_FAILED(rv)) {
             inputStream->Close();
             return rv;
         }
 
         /*
          * We have to backtrack from the end of the file until we find the
          * CDS signature
          */
         // We know it's at least this far from the end
         for (PRUint32 pos = length - ZIP_EOCDR_HEADER_SIZE;
              (PRInt32)pos >= 0; pos--) {
-            PRUint32 sig = PEEK32((unsigned char *)buf + pos);
+            PRUint32 sig = PEEK32(buf + pos);
             if (sig == ZIP_EOCDR_HEADER_SIGNATURE) {
                 // Skip down to entry count
                 pos += 10;
                 PRUint32 entries = READ16(buf, &pos);
                 // Skip past CDS size
                 pos += 4;
                 mCDSOffset = READ32(buf, &pos);
                 PRUint32 commentlen = READ16(buf, &pos);
 
                 if (commentlen == 0)
                     mComment.Truncate();
                 else if (pos + commentlen <= length)
-                    mComment.Assign(buf + pos, commentlen);
+                    mComment.Assign((const char *)buf + pos, commentlen);
                 else {
                     if ((seek + pos + commentlen) > size) {
                         inputStream->Close();
                         return NS_ERROR_FILE_CORRUPTED;
                     }
                     nsAutoArrayPtr<char> field(new char[commentlen]);
                     NS_ENSURE_TRUE(field, NS_ERROR_OUT_OF_MEMORY);
                     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
@@ -677,28 +677,28 @@ NS_IMETHODIMP nsZipWriter::Close()
             nsresult rv = mHeaders[i]->WriteCDSHeader(mStream);
             if (NS_FAILED(rv)) {
                 Cleanup();
                 return rv;
             }
             size += mHeaders[i]->GetCDSHeaderLength();
         }
 
-        char buf[ZIP_EOCDR_HEADER_SIZE];
+        PRUint8 buf[ZIP_EOCDR_HEADER_SIZE];
         PRUint32 pos = 0;
         WRITE32(buf, &pos, ZIP_EOCDR_HEADER_SIGNATURE);
         WRITE16(buf, &pos, 0);
         WRITE16(buf, &pos, 0);
         WRITE16(buf, &pos, mHeaders.Count());
         WRITE16(buf, &pos, mHeaders.Count());
         WRITE32(buf, &pos, size);
         WRITE32(buf, &pos, mCDSOffset);
         WRITE16(buf, &pos, mComment.Length());
 
-        nsresult rv = ZW_WriteData(mStream, buf, pos);
+        nsresult rv = ZW_WriteData(mStream, (const char *)buf, pos);
         if (NS_FAILED(rv)) {
             Cleanup();
             return rv;
         }
 
         rv = ZW_WriteData(mStream, mComment.get(), mComment.Length());
         if (NS_FAILED(rv)) {
             Cleanup();
--- a/modules/libjar/zipwriter/test/unit/head_zipwriter.js
+++ b/modules/libjar/zipwriter/test/unit/head_zipwriter.js
@@ -50,22 +50,20 @@ const PR_TRUNCATE    = 0x20
 const PR_SYNC        = 0x40
 const PR_EXCL        = 0x80
 
 const ZIP_EOCDR_HEADER_SIZE = 22;
 const ZIP_FILE_HEADER_SIZE = 30;
 const ZIP_CDS_HEADER_SIZE = 46;
 const ZIP_METHOD_STORE = 0
 const ZIP_METHOD_DEFLATE = 8
+const ZIP_EXTENDED_TIMESTAMP_SIZE = 9;
 
 const PR_USEC_PER_MSEC = 1000;
 
-// ZIP times are stored at a 2 second resolution.
-const TIME_RESOLUTION = 2000;
-
 const DATA_DIR = "data/";
 
 var ZipWriter = Components.Constructor("@mozilla.org/zipwriter;1",
                                        "nsIZipWriter");
 var ZipReader = Components.Constructor("@mozilla.org/libjar/zip-reader;1",
                                        "nsIZipReader", "open");
 
 var dirSvc = Cc["@mozilla.org/file/directory_service;1"]
--- a/modules/libjar/zipwriter/test/unit/test_asyncadd.js
+++ b/modules/libjar/zipwriter/test/unit/test_asyncadd.js
@@ -72,21 +72,18 @@ var observer = {
     for (var i = 0; i < TESTS.length; i++) {
       do_check_true(zipR.hasEntry(TESTS[i].name));
 
       var source = do_get_file(DATA_DIR + TESTS[i].name);
       var entry = zipR.getEntry(TESTS[i].name);
       do_check_eq(entry.realSize, TESTS[i].size);
       do_check_eq(entry.size, TESTS[i].size);
       do_check_eq(entry.CRC32, TESTS[i].crc);
-
-      var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) -
-                          source.lastModifiedTime);
-      if (diff > TIME_RESOLUTION)
-        do_throw(diff);
+      do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC,
+                  source.lastModifiedTime);
 
       zipR.test(TESTS[i].name);
     }
 
     zipR.close();
     do_test_finished();
   }
 };
@@ -95,14 +92,15 @@ function run_test()
 {
   zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
 
   for (var i = 0; i < TESTS.length; i++) {
     var source = do_get_file(DATA_DIR+TESTS[i].name);
     zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, source,
                       true);
     size += ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+            (ZIP_EXTENDED_TIMESTAMP_SIZE * 2) +
             (TESTS[i].name.length*2) + TESTS[i].size;
   }
   do_test_pending();
   zipW.processQueue(observer, null);
   do_check_true(zipW.inQueue);
 }
--- a/modules/libjar/zipwriter/test/unit/test_bug419769_1.js
+++ b/modules/libjar/zipwriter/test/unit/test_bug419769_1.js
@@ -50,19 +50,17 @@ function testpass(source)
   var entry = source.getEntry(FILENAME);
   do_check_neq(entry, null);
 
   do_check_false(entry.isDirectory);
 
   // Should be stored
   do_check_eq(entry.compression, ZIP_METHOD_DEFLATE);
 
-  var diff = Math.abs((entry.lastModifiedTime / PR_USEC_PER_MSEC) - time);
-  if (diff > TIME_RESOLUTION)
-    do_throw(diff);
+  do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, time);
 
   // File size should match our data size.
   do_check_eq(entry.realSize, DATA.length);
 
   // Check that the CRC is accurate
   do_check_eq(entry.CRC32, CRC);
 }
 
--- a/modules/libjar/zipwriter/test/unit/test_bug425768.js
+++ b/modules/libjar/zipwriter/test/unit/test_bug425768.js
@@ -52,11 +52,13 @@ function run_test()
   zipW.close();
 
   var zipR = new ZipReader(tmpFile);
   do_check_true(zipR.hasEntry(DIRNAME));
   zipR.close();
 
   // Adding the directory would have added a fixed amount to the file size.
   // Any difference suggests the CDS was written out incorrectly.
-  var extra = ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE + (DIRNAME.length * 2);
+  var extra = ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+              (DIRNAME.length * 2) + (ZIP_EXTENDED_TIMESTAMP_SIZE * 2);
+
   do_check_eq(source.fileSize + extra, tmpFile.fileSize);
 }
--- a/modules/libjar/zipwriter/test/unit/test_deflatedata.js
+++ b/modules/libjar/zipwriter/test/unit/test_deflatedata.js
@@ -60,19 +60,17 @@ function run_test()
   var entry = zipW.getEntry(FILENAME);
 
   do_check_true(entry != null);
 
   // Check entry seems right.
   do_check_eq(entry.compression, ZIP_METHOD_DEFLATE);
   do_check_eq(entry.CRC32, CRC);
   do_check_eq(entry.realSize, DATA.length);
-  var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) - time);
-  if (diff > TIME_RESOLUTION)
-    do_throw(diff);
+  do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, time);
 
   zipW.close();
 
   // Test the stored data with the zipreader
   var zipR = new ZipReader(tmpFile);
   do_check_true(zipR.hasEntry(FILENAME));
 
   zipR.test(FILENAME);
--- a/modules/libjar/zipwriter/test/unit/test_editexisting.js
+++ b/modules/libjar/zipwriter/test/unit/test_editexisting.js
@@ -37,23 +37,23 @@
  */
 
 // Values taken from using zipinfo to list the test.zip contents
 var TESTS = [
   {
     name: "test.txt",
     size: 232,
     crc: 0x0373ac26,
-    time: new Date(2007, 4, 1, 21, 44, 56)
+    time: Date.UTC(2007, 4, 1, 20, 44, 55)
   },
   {
     name: "test.png",
     size: 3402,
     crc: 0x504a5c30,
-    time: new Date(2007, 4, 1, 21, 49, 40)
+    time: Date.UTC(2007, 4, 1, 20, 49, 39)
   }
 ];
 var BADENTRY = "unknown.txt";
 
 function run_test()
 {
   // Copy our test zip to the tmp dir so we can modify it
   var testzip = do_get_file(DATA_DIR + "test.zip");
@@ -65,20 +65,17 @@ function run_test()
 
   for (var i = 0; i < TESTS.length; i++) {
     do_check_true(zipW.hasEntry(TESTS[i].name));
     var entry = zipW.getEntry(TESTS[i].name);
     do_check_true(entry != null);
 
     do_check_eq(entry.realSize, TESTS[i].size);
     do_check_eq(entry.CRC32, TESTS[i].crc);
-    var diff = Math.abs(TESTS[i].time -
-               (entry.lastModifiedTime / PR_USEC_PER_MSEC));
-    if (diff > TIME_RESOLUTION)
-      do_throw(diff);
+    do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, TESTS[i].time);
   }
 
   try {
     zipW.removeEntry(BADENTRY, false);
     do_throw("shouldn't be able to remove an entry that doesn't exist");
   }
   catch (e) {
     do_check_eq(e.result, Components.results.NS_ERROR_FILE_NOT_FOUND);
--- a/modules/libjar/zipwriter/test/unit/test_storedata.js
+++ b/modules/libjar/zipwriter/test/unit/test_storedata.js
@@ -51,19 +51,17 @@ function testpass(source)
   var entry = source.getEntry(FILENAME);
   do_check_neq(entry, null);
 
   do_check_false(entry.isDirectory);
 
   // Should be stored
   do_check_eq(entry.compression, ZIP_METHOD_STORE);
 
-  var diff = Math.abs((entry.lastModifiedTime / PR_USEC_PER_MSEC) - time);
-  if (diff > TIME_RESOLUTION)
-    do_throw(diff);
+  do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC, time);
 
   // File size should match our data size.
   do_check_eq(entry.realSize, DATA.length);
   // When stored sizes should match.
   do_check_eq(entry.size, entry.realSize);
 
   // Check that the CRC is accurate
   do_check_eq(entry.CRC32, CRC);
@@ -85,16 +83,17 @@ function run_test()
                       Ci.nsIZipWriter.COMPRESSION_NONE, stream, false);
 
   // Check that zip state is right at this stage.
   testpass(zipW);
   zipW.close();
 
   do_check_eq(tmpFile.fileSize,
               DATA.length + ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+              (ZIP_EXTENDED_TIMESTAMP_SIZE * 2) +
               (FILENAME.length * 2) + ZIP_EOCDR_HEADER_SIZE);
 
   // Check to see if we get the same results loading afresh.
   zipW.open(tmpFile, PR_RDWR);
   testpass(zipW);
   zipW.close();
 
   // Test the stored data with the zipreader
--- a/modules/libjar/zipwriter/test/unit/test_sync.js
+++ b/modules/libjar/zipwriter/test/unit/test_sync.js
@@ -55,16 +55,17 @@ function run_test()
   zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
 
   var size = 0;
   for (var i = 0; i < TESTS.length; i++) {
     var source = do_get_file(DATA_DIR + TESTS[i].name);
     zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, source,
                       false);
     size += ZIP_FILE_HEADER_SIZE + ZIP_CDS_HEADER_SIZE +
+            (ZIP_EXTENDED_TIMESTAMP_SIZE * 2) +
             (TESTS[i].name.length*2) + TESTS[i].size;
   }
 
   zipW.close();
   size += ZIP_EOCDR_HEADER_SIZE;
 
   do_check_eq(size, tmpFile.fileSize);
 
@@ -74,19 +75,16 @@ function run_test()
   for (var i = 0; i < TESTS.length; i++) {
     var source = do_get_file(DATA_DIR + TESTS[i].name);
     do_check_true(zipR.hasEntry(TESTS[i].name));
 
     var entry = zipR.getEntry(TESTS[i].name);
     do_check_eq(entry.realSize, TESTS[i].size);
     do_check_eq(entry.size, TESTS[i].size);
     do_check_eq(entry.CRC32, TESTS[i].crc);
-
-    var diff = Math.abs((entry.lastModifiedTime/PR_USEC_PER_MSEC) -
-                        source.lastModifiedTime);
-    if (diff > TIME_RESOLUTION)
-      do_throw(diff);
+    do_check_eq(entry.lastModifiedTime / PR_USEC_PER_MSEC,
+                source.lastModifiedTime);
 
     zipR.test(TESTS[i].name);
   }
 
   zipR.close();
 }