Formatting dates in XLSX.utils.sheet_to_json makes date lose 1 day #3019

Closed
opened 2023-10-20 11:22:58 +00:00 by Blanay · 13 comments

Hello !
I know this is a topic that has been mentionned multiple times but I can't seem to find a fix.
Here's the code :
const workbook = XLSX.read(data, {type: 'array', cellDates: true, cellText: false, cellNF: true});
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});

Everytime I try to run this code, I get the right date format, but the date loses one day.
For example, in the original XLSX file:
image

But once I print the jsonData, I get:
image
image

Any solution to this ?
I'm using the 0.20.0 version's standalone file on Chrome (that was supposed to fix this issue):
https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js

Hello ! I know this is a topic that has been mentionned multiple times but I can't seem to find a fix. Here's the code : `const workbook = XLSX.read(data, {type: 'array', cellDates: true, cellText: false, cellNF: true});` `const firstSheetName = workbook.SheetNames[0];` `const worksheet = workbook.Sheets[firstSheetName];` `const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});` Everytime I try to run this code, I get the right date format, but the date loses one day. For example, in the original XLSX file: ![image](/attachments/3a4aa285-9e5b-4160-a1c4-3b353de431a4) But once I print the jsonData, I get: ![image](/attachments/d0c90264-2e62-4f50-a1ab-636d765bb4bb) ![image](/attachments/2d749407-1c97-46be-9e33-3ce2d7a3f8a0) Any solution to this ? I'm using the 0.20.0 version's standalone file on Chrome (that was supposed to fix this issue): https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js
1.8 KiB
2.3 KiB
2.3 KiB
Owner

Can you share the file?

Can you share the file?
Author

If you're talking about the XLSX file, here's the one I did the tests with.

If you're talking about the XLSX file, here's the one I did the tests with.
Owner

Assuming you are talking about column E, can you display the raw date before generating the final result?

const workbook = XLSX.read(data, {type: 'array', cellDates: true, cellText: false, cellNF: true});
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];

console.log(JSON.stringify(worksheet["E2"]));
console.log(JSON.stringify(worksheet["E3"]));

Also, since you are passing cellNF: true, the default date format will be added to cells. You need to wipe the number format for date cells after reading and before generating the data:

const range = XLSX.utils.decode_range(worksheet["!ref"]);
for(let R = range.s.r; R <= range.e.r; ++R) {
  for(let C = range.s.c; C <= range.e.c; ++C) {
    const addr = XLSX.utils.encode_cell({c:C,r:R});
    if(!worksheet[addr] || worksheet[addr].t != "d") continue;
    delete worksheet[addr].z;
  }
}
Assuming you are talking about column E, can you display the raw date before generating the final result? ```js const workbook = XLSX.read(data, {type: 'array', cellDates: true, cellText: false, cellNF: true}); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; console.log(JSON.stringify(worksheet["E2"])); console.log(JSON.stringify(worksheet["E3"])); ``` Also, since you are passing `cellNF: true`, the default date format will be added to cells. You need to wipe the number format for date cells after reading and before generating the data: ```js const range = XLSX.utils.decode_range(worksheet["!ref"]); for(let R = range.s.r; R <= range.e.r; ++R) { for(let C = range.s.c; C <= range.e.c; ++C) { const addr = XLSX.utils.encode_cell({c:C,r:R}); if(!worksheet[addr] || worksheet[addr].t != "d") continue; delete worksheet[addr].z; } } ```
Author

I don't really need the cellNF: true, it was just for testing purpose. If I remove it, will I still need to use the second code snippet that you sent me ?
Here's the raw date before generating the final result using the first code snippet (without cellNF: true):
image

Same thing but with cellNF: true
image

I don't really need the cellNF: true, it was just for testing purpose. If I remove it, will I still need to use the second code snippet that you sent me ? Here's the raw date before generating the final result using the first code snippet (without cellNF: true): ![image](/attachments/451436b5-ff1e-4e73-b414-d6ea52e963b9) Same thing but with cellNF: true ![image](/attachments/39695b56-5653-479a-a65d-f2bb59caf99c)
Owner

We are unable to reproduce the issue locally. Can you also share your browser locale? Run the following in the console

console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)

NodeJS

Sample script:

const XLSX = require("xlsx");
const workbook = XLSX.readFile("Template_upload_passagers.xlsx", {cellDates: true, cellText: false, cellNF: true});
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});
console.log(jsonData.map(row => row.BirthDate));

Save that script to i3019.js, copy your template to the same directory, run npm i --save https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz, then run node i3019.js.

This prints the dates using the m/d/yy format:

[ '12/24/02', '12/25/02' ]

Modified Date Formats

If you add that loop:

const XLSX = require("xlsx");
const workbook = XLSX.readFile("Template_upload_passagers.xlsx", {cellDates: true, cellText: false, cellNF: true});
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
const range = XLSX.utils.decode_range(worksheet["!ref"]);
for(let R = range.s.r; R <= range.e.r; ++R) {
  for(let C = range.s.c; C <= range.e.c; ++C) {
    const addr = XLSX.utils.encode_cell({c:C,r:R});
    if(!worksheet[addr] || worksheet[addr].t != "d") continue;
    delete worksheet[addr].z;
  }
}
const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});
console.log(jsonData.map(row => row.BirthDate));

you will see the dates in the format you specified:

[ '24/12/2002', '25/12/2002' ]

Browser

Save the following to index.html:

<body>
<script src="https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js"></script>
<script>
(async() => {
  const f = await fetch("Template_upload_passagers.xlsx");
  const ab = await f.arrayBuffer();
  const workbook = XLSX.read(ab, {cellDates: true, cellText: false, cellNF: true});
  const firstSheetName = workbook.SheetNames[0];
  const worksheet = workbook.Sheets[firstSheetName];
  const range = XLSX.utils.decode_range(worksheet["!ref"]);
  const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});
  console.log(jsonData.map(row => row.BirthDate));  
})();
</script>
</body>

Copy the Template_upload_passagers.xlsx file into the same folder, then start a local web server:

npx http-server .

It will display a URL (typically http://localhost:8080). Open that URL in a web browser and check the console. Chrome 117 shows:

i3019-a.png

Modified Date Formats

If you add that loop:

<body>
<script src="https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js"></script>
<script>
(async() => {
  const f = await fetch("Template_upload_passagers.xlsx");
  const ab = await f.arrayBuffer();
  const workbook = XLSX.read(ab, {cellDates: true, cellText: false, cellNF: true});
  const firstSheetName = workbook.SheetNames[0];
  const worksheet = workbook.Sheets[firstSheetName];
  const range = XLSX.utils.decode_range(worksheet["!ref"]);
  for(let R = range.s.r; R <= range.e.r; ++R) {
   for(let C = range.s.c; C <= range.e.c; ++C) {
      const addr = XLSX.utils.encode_cell({c:C,r:R});
      if(!worksheet[addr] || worksheet[addr].t != "d") continue;
      delete worksheet[addr].z;
    }
  }
  const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});
  console.log(jsonData.map(row => row.BirthDate)); 
})();
</script>
</body>

The console will show the modified date formats:

i3019-b.png

We are unable to reproduce the issue locally. Can you also share your browser locale? Run the following in the console ```js console.log(Intl.DateTimeFormat().resolvedOptions().timeZone) ``` ### NodeJS Sample script: ```js const XLSX = require("xlsx"); const workbook = XLSX.readFile("Template_upload_passagers.xlsx", {cellDates: true, cellText: false, cellNF: true}); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'}); console.log(jsonData.map(row => row.BirthDate)); ``` Save that script to `i3019.js`, copy your template to the same directory, run `npm i --save https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz`, then run `node i3019.js`. This prints the dates using the `m/d/yy` format: ```js [ '12/24/02', '12/25/02' ] ``` #### Modified Date Formats If you add that loop: ```js const XLSX = require("xlsx"); const workbook = XLSX.readFile("Template_upload_passagers.xlsx", {cellDates: true, cellText: false, cellNF: true}); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const range = XLSX.utils.decode_range(worksheet["!ref"]); for(let R = range.s.r; R <= range.e.r; ++R) { for(let C = range.s.c; C <= range.e.c; ++C) { const addr = XLSX.utils.encode_cell({c:C,r:R}); if(!worksheet[addr] || worksheet[addr].t != "d") continue; delete worksheet[addr].z; } } const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'}); console.log(jsonData.map(row => row.BirthDate)); ``` you will see the dates in the format you specified: ```js [ '24/12/2002', '25/12/2002' ] ``` ### Browser Save the following to `index.html`: ```html <body> <script src="https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js"></script> <script> (async() => { const f = await fetch("Template_upload_passagers.xlsx"); const ab = await f.arrayBuffer(); const workbook = XLSX.read(ab, {cellDates: true, cellText: false, cellNF: true}); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const range = XLSX.utils.decode_range(worksheet["!ref"]); const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'}); console.log(jsonData.map(row => row.BirthDate)); })(); </script> </body> ``` Copy the `Template_upload_passagers.xlsx` file into the same folder, then start a local web server: ```bash npx http-server . ``` It will display a URL (typically http://localhost:8080). Open that URL in a web browser and check the console. Chrome 117 shows: ![i3019-a.png](/attachments/4cc43437-e0fe-4107-9fd2-416944933c03) #### Modified Date Formats If you add that loop: ```html <body> <script src="https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js"></script> <script> (async() => { const f = await fetch("Template_upload_passagers.xlsx"); const ab = await f.arrayBuffer(); const workbook = XLSX.read(ab, {cellDates: true, cellText: false, cellNF: true}); const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const range = XLSX.utils.decode_range(worksheet["!ref"]); for(let R = range.s.r; R <= range.e.r; ++R) { for(let C = range.s.c; C <= range.e.c; ++C) { const addr = XLSX.utils.encode_cell({c:C,r:R}); if(!worksheet[addr] || worksheet[addr].t != "d") continue; delete worksheet[addr].z; } } const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'}); console.log(jsonData.map(row => row.BirthDate)); })(); </script> </body> ``` The console will show the modified date formats: ![i3019-b.png](/attachments/3214bffe-e789-4a67-870a-5bb5f0d28234)
Author

Hi, here's the result of my browser locale:
Europe/Paris

I tried using your code, and I still get the issue.
Here's the code:

axios.get(properties.xlsx_file, {responseType: 'arraybuffer'})  // properties.xlsx_file represents the url of the file
  		.then(response => {
      	const data = response.data;
        const workbook = XLSX.read(data, {cellDates: true, cellText: false, cellNF: true});
        
        // Variables to define converted file properties
        const filename = "converted.csv";
        const mimetype = 'text/csv';

        const firstSheetName = workbook.SheetNames[0];
        const worksheet = workbook.Sheets[firstSheetName];
        const range = XLSX.utils.decode_range(worksheet["!ref"]);
        for(let R = range.s.r; R <= range.e.r; ++R) {
          for(let C = range.s.c; C <= range.e.c; ++C) {
            const addr = XLSX.utils.encode_cell({c:C,r:R});
            if(!worksheet[addr] || worksheet[addr].t != "d") continue;
            delete worksheet[addr].z;
          }
        }
        const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'});
        console.log(jsonData.map(row => row.BirthDate));

        // Converting the XLSX file to csv
		const csvData = Papa.unparse(jsonData, {delimiter: ','});
        var blob = new Blob([csvData], {type: 'text/csv'});
        var reader = new FileReader();
        
        reader.readAsDataURL(blob)

        reader.onloadend = function () {

            var base64String = reader.result;
            var base64Substring = base64String.substr(base64String.indexOf(',') + 1)
            console.log(base64Substring);
            context.uploadContent(filename, base64Substring, uploadFile);

        }

  })
  .catch(error => {
    console.error('Error fetching the XLSX file:', error);
  });


function uploadFile(err, url){
    	instance.publishState('csv_file', url);
        if(err){
        	console.log(err);
        }
}

To quickly explain the rest of the code, I'm trying to convert an xlsx file to a csv and upload it into a database (I'm building a plugin for a No-Code application on the Bubble.io platform, hence the properties.xlsx_file, context.uploadContent and instance.publishState. Don't mind those.)
Running the above code and looking into the browser console gives me the following:
image

Could it be because of my browser locale ?

Hi, here's the result of my browser locale: Europe/Paris I tried using your code, and I still get the issue. Here's the code: ```javascript axios.get(properties.xlsx_file, {responseType: 'arraybuffer'}) // properties.xlsx_file represents the url of the file .then(response => { const data = response.data; const workbook = XLSX.read(data, {cellDates: true, cellText: false, cellNF: true}); // Variables to define converted file properties const filename = "converted.csv"; const mimetype = 'text/csv'; const firstSheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[firstSheetName]; const range = XLSX.utils.decode_range(worksheet["!ref"]); for(let R = range.s.r; R <= range.e.r; ++R) { for(let C = range.s.c; C <= range.e.c; ++C) { const addr = XLSX.utils.encode_cell({c:C,r:R}); if(!worksheet[addr] || worksheet[addr].t != "d") continue; delete worksheet[addr].z; } } const jsonData = XLSX.utils.sheet_to_json(worksheet, {raw: false, dateNF: 'd"/"m"/"yyyy'}); console.log(jsonData.map(row => row.BirthDate)); // Converting the XLSX file to csv const csvData = Papa.unparse(jsonData, {delimiter: ','}); var blob = new Blob([csvData], {type: 'text/csv'}); var reader = new FileReader(); reader.readAsDataURL(blob) reader.onloadend = function () { var base64String = reader.result; var base64Substring = base64String.substr(base64String.indexOf(',') + 1) console.log(base64Substring); context.uploadContent(filename, base64Substring, uploadFile); } }) .catch(error => { console.error('Error fetching the XLSX file:', error); }); function uploadFile(err, url){ instance.publishState('csv_file', url); if(err){ console.log(err); } } ``` To quickly explain the rest of the code, I'm trying to convert an xlsx file to a csv and upload it into a database (I'm building a plugin for a No-Code application on the Bubble.io platform, hence the properties.xlsx_file, context.uploadContent and instance.publishState. Don't mind those.) Running the above code and looking into the browser console gives me the following: ![image](/attachments/2e3c391a-2501-455d-8709-8039e9f1a1fd) Could it be because of my browser locale ?
8.1 KiB
Owner

Europe/Paris is currently in Central European Standard Time which is UTC+2. Pass the option UTC: true to sheet_to_json to force the correct interpretation.

https://docs.sheetjs.com/docs/csf/features/dates#utc-option more details.

We will revisit the default option for that setting in the future.

`Europe/Paris` is currently in Central European Standard Time which is UTC+2. Pass the option `UTC: true` to `sheet_to_json` to force the correct interpretation. https://docs.sheetjs.com/docs/csf/features/dates#utc-option more details. We will revisit the default option for that setting in the future.
Owner

Reflecting on this a bit, it probably makes sense to force UTC: true when raw: false is set.

Reflecting on this a bit, it probably makes sense to force `UTC: true` when `raw: false` is set.
sheetjs reopened this issue 2023-10-20 22:33:36 +00:00
Author

Awesome ! My dates don't lose one day anymore ! Thank you very much for all the help that you've provided :)

Awesome ! My dates don't lose one day anymore ! Thank you very much for all the help that you've provided :)

UTC: true and raw: false still stealing a few seconds to the date

`UTC: true` and `raw: false` still stealing a few seconds to the date
Owner

@ruitalia can you share your current timezone and a small example?

@ruitalia can you share your current timezone and a small example?

I'm in Atlantic/Azores UTC-1 timezone.

I realize now that in 0.20.0 version the raw: false is working nice, but with UTC: true the bug remains.

I'm in Atlantic/Azores UTC-1 timezone. I realize now that in 0.20.0 version the `raw: false` is working nice, but with `UTC: true` the bug remains.
Owner

This should be live in 0.20.1, please update and follow up if the issue persists.

This should be live in 0.20.1, please update and follow up if the issue persists.
Sign in to join this conversation.
No Milestone
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: sheetjs/sheetjs#3019
No description provided.