diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c6c8b36 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.gitignore b/.gitignore index 9daeafb..3c3629e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -test +node_modules diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,2 @@ +{ +} diff --git a/.npmignore b/.npmignore index 9daeafb..44aaf22 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1,5 @@ test +.editorconfig +.jshintrc +Gruntfile.js +.DS_Store diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f94f4ed --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - "0.10" + - "0.11" +before_script: + - npm install -g grunt-cli +script: grunt test diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..82ae255 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +#Changelog + +##1.1.0 + +- Added MIT license +- Added Changelog +- Added Grunt config +- Added editorconfig +- Added Tests +- Fixed JSHint errors +- Formatted code as per editorconfig +- Fixed bug with conversion from year(s) to another unit +- Make sure `Error` (type) is thrown when invalid unit is encountered +- Added minified distributable version +- Updated package.json +- Added bower config +- Added travis config diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..30835ef --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,43 @@ +module.exports = function(grunt) { + // measure the time each task takes + require('time-grunt')(grunt); + + // autoload Grunt tasks + require('load-grunt-tasks')(grunt); + + // main project config + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + uglify: { + dist: { + files: { + 'dist/<%= pkg.name %>.min.js': '<%= pkg.name %>.js' + } + } + }, + jshint: { + options: { + jshintrc: true + }, + files: [ + 'Gruntfile.js', + '<%= pkg.name %>.js', + 'test/**/*.js' + ] + }, + mochaTest: { + test: { + options: { + reporter: 'spec' + }, + src: ['test/**/*.js'] + } + } + }); + + // user defined tasks + grunt.registerTask('test', ['mochaTest']); + grunt.registerTask('lint', ['jshint']); + grunt.registerTask('build', ['uglify']); + grunt.registerTask('default', ['jshint', 'mochaTest', 'uglify']); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..669e532 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) Michael David Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 7e5d2e6..042da42 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ #Timestring +[![Build Status](https://travis-ci.org/mike182uk/timestring.svg?branch=master)](https://travis-ci.org/mike182uk/timestring) + +[![NPM](https://nodei.co/npm/timestring.png?downloads=true&stars=true)](https://nodei.co/npm/timestring/) + Attempts to parse a human readable time string into a time based value. ##Overview @@ -158,14 +162,21 @@ console.log(daysThisWeek); // will log 5 ###Browser -All you need to do to get timestring working in the browser is download / clone this repo and make sure you include the `timestring.js` script on your page: +All you need to do to get timestring working in the browser is download / clone this repo and make sure you include the `dist/timestring.min.js` script on your page: ```html - + ``` -### Node -To install for a node project, navigate to the projects root folder and in your terminal type the following: +Alternatively you can you use bower to manage this dependency for you: + +``` +bower install timestring --save +``` + +###Node + +To install for a node application, navigate to the projects root folder and in your terminal type the following: ``` npm install timestring diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..9cf4dee --- /dev/null +++ b/bower.json @@ -0,0 +1,16 @@ +{ + "name": "timestring", + "main": "timestring.js", + "version": "1.1.0", + "description": "Parse a human readable time string into a time based value", + "homepage": "https://github.com/mike182uk/timestring", + "authors": [ + "Michael David Barrett " + ], + "license": "MIT", + "ignore": [ + "**/.*", + "node_modules", + "test" + ] +} diff --git a/dist/timestring.min.js b/dist/timestring.min.js new file mode 100644 index 0000000..470b3d1 --- /dev/null +++ b/dist/timestring.min.js @@ -0,0 +1 @@ +(function(){"use strict";var a=function(a){var b={hoursPerDay:24,daysPerWeek:7,weeksPerMonth:4,monthsPerYear:12};a=a||{},this.settings=b;for(var c in a)this.settings[c]=a[c];this.units={s:["s","sec","secs","second","seconds"],m:["m","min","mins","minute","minutes"],h:["h","hr","hrs","hour","hours"],d:["d","day","days"],w:["w","week","weeks"],mth:["mth","mths","month","months"],y:["y","yr","yrs","year","years"]},this.unitValues={s:1,m:60,h:3600},this.unitValues.d=this.settings.hoursPerDay*this.unitValues.h,this.unitValues.w=this.settings.daysPerWeek*this.unitValues.d,this.unitValues.mth=this.settings.weeksPerMonth*this.unitValues.w,this.unitValues.y=this.settings.monthsPerYear*this.unitValues.mth};a.prototype.parse=function(a,b){function c(a){for(var b in f.units)for(var c in f.units[b])if(a===f.units[b][c])return b;throw new Error("The unit ["+a+"] is not supported by timestring")}function d(a,b){var d=f.unitValues[c(b)];return a/d}function e(a,b){var d=f.unitValues[c(b)];return a*d}var f=this,g=0,h=a.toLowerCase().replace(/[^\.\w+-]+/g,"").match(/[-+]?[0-9]+[a-z]+/g);if(null!==h)for(var i=0;i", - "repository" : { - "type": "git", - "url": "git://github.com/mike182uk/timestring.git" - }, - "main" : "timestring.js", - "version" : "1.0.2" + "name": "timestring", + "description": "Parse a human readable time string into a time based value", + "homepage": "https://github.com/mike182uk/timestring", + "author": "Michael David Barrett ", + "repository": { + "type": "git", + "url": "git://github.com/mike182uk/timestring.git" + }, + "main": "timestring.js", + "version": "1.1.0", + "devDependencies": { + "chai": "^1.9.1", + "grunt": "^0.4.5", + "grunt-contrib-jshint": "^0.10.0", + "grunt-contrib-uglify": "^0.5.0", + "grunt-mocha-test": "^0.11.0", + "load-grunt-tasks": "^0.6.0", + "mocha": "^1.20.1", + "time-grunt": "^0.4.0" + } } diff --git a/test/timestring.js b/test/timestring.js new file mode 100644 index 0000000..d27ec9d --- /dev/null +++ b/test/timestring.js @@ -0,0 +1,75 @@ +var chai = require('chai'); +var expect = chai.expect; +var timestring = require('../timestring'); + +describe('timestring', function() { + it('can parse a timestring', function(done) { + var ts = new timestring(); + + expect(ts.parse('1s')).to.equal(1); + expect(ts.parse('1m')).to.equal(60); + expect(ts.parse('1h')).to.equal(3600); + expect(ts.parse('1d')).to.equal(86400); + expect(ts.parse('1w')).to.equal(604800); + expect(ts.parse('1mth')).to.equal(2419200); + expect(ts.parse('1y')).to.equal(29030400); + + done(); + }); + + it('can return a value in a specified unit', function(done) { + expect((new timestring()).parse('5m', 's')).to.equal(300); + expect((new timestring()).parse('5m', 'm')).to.equal(5); + + done(); + }); + + it('uses the passed settings instead of the defaults', function(done) { + var settings = { + hoursPerDay: 1, + daysPerWeek: 2, + weeksPerMonth: 3, + monthsPerYear: 4 + }; + + var ts = new timestring(settings); + + expect(ts.parse('1d', 'h')).to.equal(1); + expect(ts.parse('1w', 'd')).to.equal(2); + expect(ts.parse('1mth', 'w')).to.equal(3); + expect(ts.parse('1y', 'mth')).to.equal(4); + + + done(); + }); + + it('throws an error when an invalid unit is used in the timestring', function(done) { + var ts = new timestring(); + + expect(ts.parse.bind(ts, '1g')).to.throw(Error); + + done(); + }); + + it('can parse a messy time string', function(done) { + expect((new timestring()).parse('5 D a YS 4 h 2 0 mI nS')).to.equal(447600); + + done(); + }); + + it('should expose a method on String.prototype that will parse the string as a timestring', function(done){ + var str = '1min'; + + // no arguments passed + expect(str.parseTime()).to.equal(60); + + // units argument passed + expect(str.parseTime('m')).to.equal(1); + + // units + settings argument passed + str = '5h'; + expect(str.parseTime('d', { hoursPerDay: 5 })).to.equal(1); + + done(); + }); +}); diff --git a/timestring.js b/timestring.js index fe53b7b..b0249b2 100644 --- a/timestring.js +++ b/timestring.js @@ -1,111 +1,112 @@ (function(){ - "use strict"; + "use strict"; - var Timestring = function(settings) { - // default settings - var defaults = { - hoursPerDay: 24, - daysPerWeek: 7, - weeksPerMonth: 4, - monthsPerYear: 12 - }; - - // merge default settings with user settings - var settings = settings || {}; - this.settings = {}; - for (var property in defaults) { this.settings[property] = defaults[property]; } - for (var property in settings) { this.settings[property] = settings[property]; } - - // time units - this.units = { - s: ['s', 'sec', 'secs', 'second', 'seconds'], - m: ['m', 'min', 'mins', 'minute', 'minutes'], - h: ['h', 'hr', 'hrs', 'hour', 'hours'], - d: ['d', 'day', 'days'], - w: ['w', 'week', 'weeks'], - mth: ['mth', 'mths','month', 'months'], - y: ['y', 'yr', 'yrs', 'year', 'years'] - }; - - // time unit seconds mappings - this.unitValues = { - s: 1, - m: 60, - h: 3600 - }; - - // dynamic time unit seconds mappings - // these are dynamic based on the settings - this.unitValues.d = this.settings.hoursPerDay * this.unitValues.h; - this.unitValues.w = this.settings.daysPerWeek * this.unitValues.d; - this.unitValues.mth = this.settings.weeksPerMonth * this.unitValues.w; - this.unitValues.y = this.settings.monthsPerYear * this.unitValues.w; + var Timestring = function(settings) { + // default settings + var defaults = { + hoursPerDay: 24, + daysPerWeek: 7, + weeksPerMonth: 4, + monthsPerYear: 12 }; - Timestring.prototype.parse = function(string, returnUnit) { - // reference to this - var self = this; + // merge default settings with user settings + settings = settings || {}; + this.settings = defaults; + for (var s in settings) { this.settings[s] = settings[s]; } - // get unit key helper - function getUnitKey(unit) { - for (var key in self.units) { - for (var u in self.units[key]) { - if (unit === self.units[key][u]) { - return key; - } - } - } + // time units + this.units = { + s: ['s', 'sec', 'secs', 'second', 'seconds'], + m: ['m', 'min', 'mins', 'minute', 'minutes'], + h: ['h', 'hr', 'hrs', 'hour', 'hours'], + d: ['d', 'day', 'days'], + w: ['w', 'week', 'weeks'], + mth: ['mth', 'mths','month', 'months'], + y: ['y', 'yr', 'yrs', 'year', 'years'] + }; - // throw exception if invalid unit is passed - throw 'The unit [' + unit + '] is not supported by timestring'; + // time unit seconds mappings + this.unitValues = { + s: 1, + m: 60, + h: 3600 + }; + + // dynamic time unit seconds mappings + // these are dynamic based on the settings + this.unitValues.d = this.settings.hoursPerDay * this.unitValues.h; + this.unitValues.w = this.settings.daysPerWeek * this.unitValues.d; + this.unitValues.mth = this.settings.weeksPerMonth * this.unitValues.w; + this.unitValues.y = this.settings.monthsPerYear * this.unitValues.mth; + }; + + Timestring.prototype.parse = function(string, returnUnit) { + // reference to this + var that = this; + + // get unit key helper + function getUnitKey(unit) { + for (var k in that.units) { + for (var u in that.units[k]) { + if (unit === that.units[k][u]) { + return k; + } } + } - // convert a value to a specific unit - function convert(value, unit) { - var baseValue = self.unitValues[getUnitKey(unit)]; - return value / baseValue; - } - - // get a value in seconds based on a specific unit - function getSeconds(value, unit) { - var baseValue = self.unitValues[getUnitKey(unit)]; - return value * baseValue; - } - - // seconds counter - var totalSeconds = 0; - - // split string into groups and get total seconds for each group - var groups = string - .toLowerCase() // convert words to lower case - .replace(/[^\.\w+-]+/g, '') // remove white space - .match(/[-+]?[0-9]+[a-z]+/g); // match time groups (digit followed by time unit - i.e 5d 15m = 2 time groups) - - if (groups !== null) { - for(var i = 0; i < groups.length; i++) { - var g = groups[i]; - var value = g.match(/[0-9]+/g)[0]; - var unit = g.match(/[a-z]+/g)[0]; - - totalSeconds += getSeconds(value, unit); - } - } - - // return total, convert if needed - return (returnUnit) ? convert(totalSeconds, returnUnit) : totalSeconds; + // throw error if invalid unit was passed + throw new Error('The unit [' + unit + '] is not supported by timestring'); } - // add convenience method to string prototype - String.prototype.parseTime = function (unit, settings) { - return (new Timestring(settings)).parse(this, unit); + // convert a value to a specific unit + function convert(value, unit) { + var baseValue = that.unitValues[getUnitKey(unit)]; + + return value / baseValue; } - // export Timestring object for either the browser or node - if (typeof module !== 'undefined' && module.exports) { - module.exports = Timestring; + // get a value in seconds based on a specific unit + function getSeconds(value, unit) { + var baseValue = that.unitValues[getUnitKey(unit)]; + + return value * baseValue; } - else { - this.Timestring = Timestring; + + // seconds counter + var totalSeconds = 0; + + // split string into groups and get total seconds for each group + var groups = string + .toLowerCase() // convert words to lower case + .replace(/[^\.\w+-]+/g, '') // remove white space + .match(/[-+]?[0-9]+[a-z]+/g); // match time groups (digit followed by time unit - i.e 5d 15m = 2 time groups) + + if (groups !== null) { + for(var i = 0; i < groups.length; i++) { + var g = groups[i]; + var value = g.match(/[0-9]+/g)[0]; + var unit = g.match(/[a-z]+/g)[0]; + + totalSeconds += getSeconds(value, unit); + } } + // return total, convert if needed + return (returnUnit) ? convert(totalSeconds, returnUnit) : totalSeconds; + }; + + // add convenience method to string prototype + String.prototype.parseTime = function (unit, settings) { + return (new Timestring(settings)).parse(this, unit); + }; + + // export Timestring object + if (typeof module !== 'undefined' && module.exports) { + module.exports = Timestring; + } + else { + this.Timestring = Timestring; + } + }).call(this);