├── .github └── workflows │ ├── ci.yml │ ├── codeql-analysis.yml │ └── release.yml ├── .gitignore ├── .releaserc.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.d.ts ├── index.js ├── package-lock.json ├── package.json └── test └── index.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: 12 13 | - env: 14 | CI: true 15 | run: npm ci 16 | - env: 17 | CI: true 18 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 21 | run: npm test 22 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '31 15 * * 3' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | publish: 10 | name: Publish to Github & NPM or Github Package Registry 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 14 17 | - env: 18 | CI: true 19 | run: npm ci 20 | - if: success() 21 | env: 22 | CI: true 23 | GH_TOKEN: ${{ secrets.GH_TOKEN }} 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | run: npx semantic-release 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /.releaserc.yaml: -------------------------------------------------------------------------------- 1 | branch: master 2 | ci: false 3 | dryRun: false 4 | debug: false 5 | extends: "semantic-release-npm-github-publish" 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.5](https://github.com/oleg-koval/excel-date-to-js/compare/v1.1.4...v1.1.5) (2022-08-19) 2 | 3 | ## [1.1.4](https://github.com/oleg-koval/excel-date-to-js/compare/v1.1.3...v1.1.4) (2021-09-23) 4 | 5 | ## [1.1.3](https://github.com/oleg-koval/excel-date-to-js/compare/v1.1.2...v1.1.3) (2021-09-23) 6 | 7 | ## [1.1.2](https://github.com/oleg-koval/excel-date-to-js/compare/v1.1.1...v1.1.2) (2021-09-23) 8 | 9 | ## [1.1.1](https://github.com/oleg-koval/excel-date-to-js/compare/v1.1.0...v1.1.1) (2021-09-23) 10 | 11 | # [1.1.0](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.7...v1.1.0) (2021-09-23) 12 | 13 | 14 | ### Features 15 | 16 | * add ability to transform js date back to excel ([#20](https://github.com/oleg-koval/excel-date-to-js/issues/20)) ([119ce12](https://github.com/oleg-koval/excel-date-to-js/commit/119ce121bcc16e3f030c1fdd4e34464884b1b115)) 17 | 18 | ## [1.0.7](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.6...v1.0.7) (2021-09-23) 19 | 20 | ## [1.0.6](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.5...v1.0.6) (2020-08-19) 21 | 22 | ## [1.0.5](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.4...v1.0.5) (2020-08-19) 23 | 24 | ## [1.0.4](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.3...v1.0.4) (2020-08-19) 25 | 26 | ## [1.0.3](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.2...v1.0.3) (2020-08-19) 27 | 28 | ## [1.0.2](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.1...v1.0.2) (2019-12-20) 29 | 30 | ## [1.0.1](https://github.com/oleg-koval/excel-date-to-js/compare/v1.0.0...v1.0.1) (2019-10-04) 31 | 32 | # 1.0.0 (2019-10-04) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * **ci:** add ci/cd pipelines, semantic-release integration ([493877a](https://github.com/oleg-koval/excel-date-to-js/commit/493877a)) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oleg Koval 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # excel-date-to-js 2 | 3 |

4 | 5 | CI 6 | 7 | 8 | Version 9 | 10 | 11 | Documentation 12 | 13 | 14 | Maintenance 15 | 16 | 17 | License: MIT 18 | 19 |

20 | 21 | ## Description 22 | 23 | Converts Excel date in integer format into JS date. Dates are stored as numbers in Excel and count the number of days since January 0, 1900 (1900 standard, for mac it is 1904, which means January 0, 1904 is the start date). Times are handled internally as numbers between 0 and 1. 24 | 25 | ## Install 26 | 27 | ```sh 28 | npm install -s excel-date-to-js 29 | ``` 30 | 31 | ## Usage 32 | 33 | ```javascript 34 | const { getJsDateFromExcel } = require("excel-date-to-js"); 35 | getJsDateFromExcel("42510"); 36 | // 2016-05-20T00:00:00.000Z 37 | ``` 38 | 39 | [Inspired by...](https://gist.github.com/christopherscott/2782634) 40 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | // thanks @moltar for contribution 2 | // https://github.com/oleg-koval/excel-date-to-js/issues/16 3 | 4 | declare module "excel-date-to-js" { 5 | export function getJsDateFromExcel(excelDate: string | number): Date 6 | export function getExcelDateFromJs(date: Date): number 7 | } 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const SECONDS_IN_DAY = 24 * 60 * 60; 2 | const MISSING_LEAP_YEAR_DAY = SECONDS_IN_DAY * 1000; 3 | const MAGIC_NUMBER_OF_DAYS = (25567 + 2); 4 | 5 | const isDate = date => Object.prototype.toString.call(date) === "[object Date]" && !isNaN(date.getTime()) 6 | 7 | /** 8 | * JavaScript dates can be constructed by passing milliseconds 9 | * since the Unix epoch (January 1, 1970) example: new Date(12312512312); 10 | * 1. Subtract number of days between: 11 | * Jan 1, 1900 and Jan 1, 1970, plus 2 ("excel leap year bug") 12 | * 2. Convert to milliseconds. 13 | * 14 | * @method getJsDateFromExcel 15 | * @param {Number} excelDate 16 | * @return {Date} 17 | */ 18 | module.exports.getJsDateFromExcel = (excelDate) => { 19 | if (!Number(excelDate)) { 20 | throw new Error('wrong input format') 21 | } 22 | 23 | const delta = excelDate - MAGIC_NUMBER_OF_DAYS; 24 | const parsed = delta * MISSING_LEAP_YEAR_DAY; 25 | const date = new Date(parsed) 26 | 27 | if (!isDate(date)) { 28 | throw new Error('wrong excel date input') 29 | } 30 | 31 | return date 32 | } 33 | 34 | module.exports.getExcelDateFromJs = (date) => { 35 | if (!isDate(date)) { 36 | throw new Error('wrong input format') 37 | } 38 | 39 | return (date.getTime() / MISSING_LEAP_YEAR_DAY) + MAGIC_NUMBER_OF_DAYS 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excel-date-to-js", 3 | "version": "1.1.5", 4 | "private": false, 5 | "description": "Convert Excel date in integer format into JS date. Dates are stored as numbers in Excel and count the number of days since January 0, 1900 (1900 standard, for mac it is 1904, which means January 0, 1904 is the start date). Times are handled internally as numbers between 0 and 1.", 6 | "main": "index.js", 7 | "engines": { 8 | "node": ">=4.2.4" 9 | }, 10 | "scripts": { 11 | "test": "npx tape ./test/**.js", 12 | "semantic-release": "semantic-release" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/oleg-koval/excel-date-to-js.git" 17 | }, 18 | "keywords": [ 19 | "excel", 20 | "excel date", 21 | "date", 22 | "microsoft", 23 | "mac", 24 | "convert date", 25 | "convert", 26 | "date convertor", 27 | "excel date convertor" 28 | ], 29 | "author": "Oleg Koval", 30 | "license": "MIT", 31 | "bugs": { 32 | "url": "https://github.com/oleg-koval/excel-date-to-js/issues" 33 | }, 34 | "homepage": "https://github.com/oleg-koval/excel-date-to-js#readme", 35 | "devDependencies": { 36 | "semantic-release": "^19.0.3", 37 | "semantic-release-npm-github-publish": "^1.1.10", 38 | "tape": "^4.8.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | const test = require('tape'); 2 | const { getJsDateFromExcel, getExcelDateFromJs } = require('../index'); 3 | 4 | test('should convert Excel date to JS date (number)', function (t) { 5 | const actual = getJsDateFromExcel(42510).toISOString(); 6 | const expected = new Date('2016-05-20T00:00:00.000Z').toISOString(); 7 | t.equal(actual, expected); 8 | t.end() 9 | }); 10 | 11 | test('should convert Excel date to JS date (string)', function (t) { 12 | const actual = getJsDateFromExcel('42510').toISOString(); 13 | const expected = new Date('2016-05-20T00:00:00.000Z').toISOString(); 14 | t.equal(actual, expected); 15 | t.end() 16 | }); 17 | 18 | test('should convert return an error on wrong input', function (t) { 19 | try { 20 | const actual = getJsDateFromExcel('text'); 21 | } catch ({ message }) { 22 | t.equal(message, 'wrong input format') 23 | } 24 | t.end() 25 | }); 26 | 27 | test('should convert return an error on wrong excel date', function (t) { 28 | try { 29 | const actual = getJsDateFromExcel(7312873827); 30 | } catch ({ message }) { 31 | t.equal(message, 'wrong excel date input') 32 | } 33 | t.end() 34 | }); 35 | 36 | test('should convert JS date to Excel date', (t) => { 37 | const date = new Date('2016-05-20T00:00:00.000Z'); 38 | const expected = 42510; 39 | 40 | const actual = getExcelDateFromJs(date); 41 | 42 | t.equal(actual, expected) 43 | 44 | t.end() 45 | }); 46 | 47 | test('should return an error on wrong input while converting JS date (wrong type)', function (t) { 48 | try { 49 | getExcelDateFromJs('text'); 50 | } catch ({ message }) { 51 | t.equal(message, 'wrong input format') 52 | t.end() 53 | } 54 | }); 55 | 56 | test('should return an error on wrong input while converting JS date (invalid date)', function (t) { 57 | try { 58 | getExcelDateFromJs(new Date('text')); 59 | } catch ({ message }) { 60 | t.equal(message, 'wrong input format') 61 | t.end() 62 | } 63 | }); 64 | --------------------------------------------------------------------------------