├── .gitignore ├── bower.json ├── package.json ├── LICENSE ├── README.md ├── specs ├── dateData.js └── humanize.spec.js └── humanize.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | node_modules 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "humanize", 3 | "version": "0.0.9", 4 | "repository": { 5 | "type": "git", 6 | "url": "git://github.com/taijinlee/humanize" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "humanize", 3 | "description": "Javascript string formatter for human readability", 4 | "homepage": "https://github.com/taijinlee/humanize", 5 | "keywords": [ 6 | "util", 7 | "client", 8 | "browser" 9 | ], 10 | "author": { 11 | "name": "Tai-Jin Lee", 12 | "email": "taijin@gmail.com" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/taijinlee/humanize.git" 17 | }, 18 | "main": "humanize.js", 19 | "version": "0.0.9-discord", 20 | "dependencies": {}, 21 | "devDependencies": { 22 | "mocha": "1.0.3", 23 | "should": "0.6.3", 24 | "jshint": "0.7.1" 25 | }, 26 | "optionalDependencies": {}, 27 | "engines": { 28 | "node": "*" 29 | }, 30 | "scripts": { 31 | "test": "./node_modules/jshint/bin/hint humanize.js; find specs -type f -a -name *.spec.js -exec ./node_modules/mocha/bin/mocha --globals requirejsVars -R list --require should {} \\;" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Tai-Jin Lee 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # humanize # 2 | 3 | Javascript data formatter for human readability. 4 | 5 | Idea, name, and initial code blatently stolen from [milanvrekic/JS-humanize](http://github.com/milanvrekic/JS-humanize) 6 | 7 | Can be loaded via AMD or in node directly. 8 | 9 | ## Installation ## 10 | 11 | npm install humanize 12 | 13 | ## Usage: ## 14 | ```javascript 15 | var humanize = require('humanize'); 16 | humanize.date('Y-m-d'); // 'yyyy-mm-dd' 17 | humanize.filesize(1234567890); // '1.15 Gb' 18 | ``` 19 | 20 | ## Functions available: ## 21 | 22 | ####humanize.noConflict()#### 23 | Give control of the "humanize" variable back to its previous owner. Returns a reference to the humanize object. 24 | 25 | ####humanize.time()#### 26 | Retrieves the current time in seconds 27 | 28 | ####humanize.date(format [, timestamp / JS Date Object = new Date()])#### 29 | This is a port of [php.js date](http://phpjs.org/functions/date:380) and behaves exactly like [PHP's date](http://php.net/manual/en/function.date.php) 30 | 31 | ####humanize.numberFormat(number [, decimals = 2, decPoint = '.', thousandsSep = ','])#### 32 | Format a number to have decimal significant decimal places, using decPoint as the decimal separator, and thousandsSep as thousands separater 33 | 34 | ####humanize.naturalDay(timestamp [, format = 'Y-m-d'])#### 35 | Returns 'today', 'tomorrow' or 'yesterday', as appropriate, otherwise format the date using the passed format with humanize.date() 36 | 37 | ####humanize.relativeTime(timestamp)#### 38 | Returns a relative time to the current time, seconds as the most granular up to years to the least granular. 39 | 40 | ####humanize.ordinal(integer)#### 41 | Converts a number into its [ordinal representation](http://en.wikipedia.org/wiki/Ordinal_number_\(linguistics\)). 42 | 43 | ####humanize.filesize(filesize [, kilo = 1024, decimals = 2, decPoint = '.', thousandsSep = ',']) #### 44 | Converts a byte count to a human readable value using kilo as the basis, and numberFormat formatting 45 | 46 | ####humanize.linebreaks(string)#### 47 | Converts a string's newlines into properly formatted html ie. one new line -> br, two new lines -> p, entire thing wrapped in p 48 | 49 | ####humanize.nl2br(string)#### 50 | Converts a string's newlines into br's 51 | 52 | ####humanize.truncatechars(string, length)#### 53 | Truncates a string to length-1 and appends '…'. If string is shorter than length, then no-op 54 | 55 | ####humanize.truncatewords(string, numWords)#### 56 | Truncates a string to only include the first numWords words and appends '…'. If string has fewer words than numWords, then no-op 57 | -------------------------------------------------------------------------------- /specs/dateData.js: -------------------------------------------------------------------------------- 1 | module.exports = function() { 2 | 3 | // all in America/Los_Angeles timezone 4 | 5 | var timestamps = { 6 | // 04/16/1986 18:23:47 7 | '514088627': { 8 | d: '16', 9 | D: 'Wed', 10 | j: '16', 11 | l: 'Wednesday', 12 | N: '3', 13 | S: 'th', 14 | w: '3', 15 | z: '105', 16 | W: '16', 17 | F: 'April', 18 | m: '04', 19 | M: 'Apr', 20 | n: '4', 21 | t: '30', 22 | L: '0', 23 | o: '1986', 24 | Y: '1986', 25 | y: '86', 26 | a: 'pm', 27 | A: 'PM', 28 | B: '141', 29 | g: '6', 30 | G: '18', 31 | h: '06', 32 | H: '18', 33 | i: '23', 34 | s: '47', 35 | u: '000000', 36 | // I: '0', 37 | O: '-0800', 38 | P: '-08:00', 39 | Z: '-28800', 40 | c: '1986-04-16T18:23:47-08:00', 41 | r: 'Wed, 16 Apr 1986 18:23:47 -0800', 42 | U: '514088627' 43 | }, 44 | 45 | // 12/25/1999 01:09:03 46 | '946112943': { 47 | d: '25', 48 | D: 'Sat', 49 | j: '25', 50 | l: 'Saturday', 51 | N: '6', 52 | S: 'th', 53 | w: '6', 54 | z: '358', 55 | W: '51', 56 | F: 'December', 57 | m: '12', 58 | M: 'Dec', 59 | n: '12', 60 | t: '31', 61 | L: '0', 62 | o: '1999', 63 | Y: '1999', 64 | y: '99', 65 | a: 'am', 66 | A: 'AM', 67 | B: '422', 68 | g: '1', 69 | G: '1', 70 | h: '01', 71 | H: '01', 72 | i: '09', 73 | s: '03', 74 | u: '000000', 75 | // I: '0', 76 | O: '-0800', 77 | P: '-08:00', 78 | Z: '-28800', 79 | c: '1999-12-25T01:09:03-08:00', 80 | r: 'Sat, 25 Dec 1999 01:09:03 -0800', 81 | U: '946112943' 82 | }, 83 | 84 | // 07/10/2011 12:23:58 85 | '1310325838': { 86 | d: '10', 87 | D: 'Sun', 88 | j: '10', 89 | l: 'Sunday', 90 | N: '7', 91 | S: 'th', 92 | w: '0', 93 | z: '190', 94 | W: '27', 95 | F: 'July', 96 | m: '07', 97 | M: 'Jul', 98 | n: '7', 99 | t: '31', 100 | L: '0', 101 | o: '2011', 102 | Y: '2011', 103 | y: '11', 104 | a: 'pm', 105 | A: 'PM', 106 | B: '849', 107 | g: '12', 108 | G: '12', 109 | h: '12', 110 | H: '12', 111 | i: '23', 112 | s: '58', 113 | u: '000000', 114 | // I: '1', 115 | O: '-0700', 116 | P: '-07:00', 117 | Z: '-25200', 118 | c: '2011-07-10T12:23:58-07:00', 119 | r: 'Sun, 10 Jul 2011 12:23:58 -0700', 120 | U: '1310325838' 121 | }, 122 | 123 | // 10/05/2015 00:00:00 124 | '1444028400': { 125 | d: '05', 126 | D: 'Mon', 127 | j: '5', 128 | l: 'Monday', 129 | N: '1', 130 | S: 'th', 131 | w: '1', 132 | z: '277', 133 | W: '41', 134 | F: 'October', 135 | m: '10', 136 | M: 'Oct', 137 | n: '10', 138 | t: '31', 139 | L: '0', 140 | o: '2015', 141 | Y: '2015', 142 | y: '15', 143 | a: 'am', 144 | A: 'AM', 145 | B: '333', 146 | g: '12', 147 | G: '0', 148 | h: '12', 149 | H: '00', 150 | i: '00', 151 | s: '00', 152 | u: '000000', 153 | // I: '1', 154 | O: '-0700', 155 | P: '-07:00', 156 | Z: '-25200', 157 | c: '2015-10-05T00:00:00-07:00', 158 | r: 'Mon, 05 Oct 2015 00:00:00 -0700', 159 | U: '1444028400' 160 | }, 161 | 162 | // 03/31/2000 13:25:14 163 | '954537914': { 164 | d: '31', 165 | D: 'Fri', 166 | j: '31', 167 | l: 'Friday', 168 | N: '5', 169 | S: 'st', 170 | w: '5', 171 | z: '90', 172 | W: '13', 173 | F: 'March', 174 | m: '03', 175 | M: 'Mar', 176 | n: '3', 177 | t: '31', 178 | L: '1', 179 | o: '2000', 180 | Y: '2000', 181 | y: '00', 182 | a: 'pm', 183 | A: 'PM', 184 | B: '934', 185 | g: '1', 186 | G: '13', 187 | h: '01', 188 | H: '13', 189 | i: '25', 190 | s: '14', 191 | u: '000000', 192 | // I: '0', 193 | O: '-0800', 194 | P: '-08:00', 195 | Z: '-28800', 196 | c: '2000-03-31T13:25:14-08:00', 197 | r: 'Fri, 31 Mar 2000 13:25:14 -0800', 198 | U: '954537914' 199 | }, 200 | 201 | // 01/02/3456 12:34:56 202 | '46893760496': { 203 | d: '02', 204 | D: 'Wed', 205 | j: '2', 206 | l: 'Wednesday', 207 | N: '3', 208 | S: 'nd', 209 | w: '3', 210 | z: '1', 211 | W: '01', 212 | F: 'January', 213 | m: '01', 214 | M: 'Jan', 215 | n: '1', 216 | t: '31', 217 | L: '1', 218 | o: '3456', 219 | Y: '3456', 220 | y: '56', 221 | a: 'pm', 222 | A: 'PM', 223 | B: '899', 224 | g: '12', 225 | G: '12', 226 | h: '12', 227 | H: '12', 228 | i: '34', 229 | s: '56', 230 | u: '000000', 231 | // I: '0', 232 | O: '-0800', 233 | P: '-08:00', 234 | Z: '-28800', 235 | c: '3456-01-02T12:34:56-08:00', 236 | r: 'Wed, 02 Jan 3456 12:34:56 -0800', 237 | U: '46893760496' 238 | }, 239 | 240 | // 01/11/1840 11:56:14 241 | '-4101509026': { 242 | d: '11', 243 | D: 'Sat', 244 | j: '11', 245 | l: 'Saturday', 246 | N: '6', 247 | S: 'th', 248 | w: '6', 249 | z: '10', 250 | W: '02', 251 | F: 'January', 252 | m: '01', 253 | M: 'Jan', 254 | n: '1', 255 | t: '31', 256 | L: '1', 257 | o: '1840', 258 | Y: '1840', 259 | y: '40', 260 | a: 'am', 261 | A: 'AM', 262 | B: '873', 263 | g: '11', 264 | G: '11', 265 | h: '11', 266 | H: '11', 267 | i: '56', 268 | s: '14', 269 | u: '000000', 270 | // I: '0', 271 | O: '-0800', 272 | P: '-08:00', 273 | Z: '-28800', 274 | c: '1840-01-11T11:56:14-08:00', 275 | r: 'Sat, 11 Jan 1840 11:56:14 -0800', 276 | U: '-4101509026' 277 | } 278 | }; 279 | 280 | return { 281 | timestamps: timestamps 282 | }; 283 | 284 | }; 285 | -------------------------------------------------------------------------------- /humanize.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | 4 | // Baseline setup 5 | // -------------- 6 | 7 | // Establish the root object, `window` in the browser, or `global` on the server. 8 | var root = this; 9 | 10 | // Save the previous value of the `humanize` variable. 11 | var previousHumanize = root.humanize; 12 | 13 | var humanize = {}; 14 | 15 | if (typeof exports !== 'undefined') { 16 | if (typeof module !== 'undefined' && module.exports) { 17 | exports = module.exports = humanize; 18 | } 19 | exports.humanize = humanize; 20 | } else { 21 | if (typeof define === 'function' && define.amd) { 22 | define('humanize', function() { 23 | return humanize; 24 | }); 25 | } 26 | root.humanize = humanize; 27 | } 28 | 29 | humanize.noConflict = function() { 30 | root.humanize = previousHumanize; 31 | return this; 32 | }; 33 | 34 | humanize.pad = function(str, count, padChar, type) { 35 | str += ''; 36 | if (!padChar) { 37 | padChar = ' '; 38 | } else if (padChar.length > 1) { 39 | padChar = padChar.charAt(0); 40 | } 41 | type = (type === undefined) ? 'left' : 'right'; 42 | 43 | if (type === 'right') { 44 | while (str.length < count) { 45 | str = str + padChar; 46 | } 47 | } else { 48 | // default to left 49 | while (str.length < count) { 50 | str = padChar + str; 51 | } 52 | } 53 | 54 | return str; 55 | }; 56 | 57 | // gets current unix time 58 | humanize.time = function() { 59 | return new Date().getTime() / 1000; 60 | }; 61 | 62 | /** 63 | * PHP-inspired date 64 | */ 65 | 66 | /* jan feb mar apr may jun jul aug sep oct nov dec */ 67 | var dayTableCommon = [ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ]; 68 | var dayTableLeap = [ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ]; 69 | // var mtable_common[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 70 | // static int ml_table_leap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 71 | 72 | 73 | humanize.date = function(format, timestamp) { 74 | var jsdate = ((timestamp === undefined) ? new Date() : // Not provided 75 | (timestamp instanceof Date) ? new Date(timestamp) : // JS Date() 76 | new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int) 77 | ); 78 | 79 | var formatChr = /\\?([a-z])/gi; 80 | var formatChrCb = function (t, s) { 81 | return f[t] ? f[t]() : s; 82 | }; 83 | 84 | var shortDayTxt = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 85 | var monthTxt = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; 86 | 87 | var f = { 88 | /* Day */ 89 | // Day of month w/leading 0; 01..31 90 | d: function () { return humanize.pad(f.j(), 2, '0'); }, 91 | 92 | // Shorthand day name; Mon..Sun 93 | D: function () { return f.l().slice(0, 3); }, 94 | 95 | // Day of month; 1..31 96 | j: function () { return jsdate.getDate(); }, 97 | 98 | // Full day name; Monday..Sunday 99 | l: function () { return shortDayTxt[f.w()]; }, 100 | 101 | // ISO-8601 day of week; 1[Mon]..7[Sun] 102 | N: function () { return f.w() || 7; }, 103 | 104 | // Ordinal suffix for day of month; st, nd, rd, th 105 | S: function () { 106 | var j = f.j(); 107 | return j > 4 && j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th'; 108 | }, 109 | 110 | // Day of week; 0[Sun]..6[Sat] 111 | w: function () { return jsdate.getDay(); }, 112 | 113 | // Day of year; 0..365 114 | z: function () { 115 | return (f.L() ? dayTableLeap[f.n()] : dayTableCommon[f.n()]) + f.j() - 1; 116 | }, 117 | 118 | /* Week */ 119 | // ISO-8601 week number 120 | W: function () { 121 | // days between midweek of this week and jan 4 122 | // (f.z() - f.N() + 1 + 3.5) - 3 123 | var midWeekDaysFromJan4 = f.z() - f.N() + 1.5; 124 | // 1 + number of weeks + rounded week 125 | return humanize.pad(1 + Math.floor(Math.abs(midWeekDaysFromJan4) / 7) + (midWeekDaysFromJan4 % 7 > 3.5 ? 1 : 0), 2, '0'); 126 | }, 127 | 128 | /* Month */ 129 | // Full month name; January..December 130 | F: function () { return monthTxt[jsdate.getMonth()]; }, 131 | 132 | // Month w/leading 0; 01..12 133 | m: function () { return humanize.pad(f.n(), 2, '0'); }, 134 | 135 | // Shorthand month name; Jan..Dec 136 | M: function () { return f.F().slice(0, 3); }, 137 | 138 | // Month; 1..12 139 | n: function () { return jsdate.getMonth() + 1; }, 140 | 141 | // Days in month; 28..31 142 | t: function () { return (new Date(f.Y(), f.n(), 0)).getDate(); }, 143 | 144 | /* Year */ 145 | // Is leap year?; 0 or 1 146 | L: function () { return new Date(f.Y(), 1, 29).getMonth() === 1 ? 1 : 0; }, 147 | 148 | // ISO-8601 year 149 | o: function () { 150 | var n = f.n(); 151 | var W = f.W(); 152 | return f.Y() + (n === 12 && W < 9 ? -1 : n === 1 && W > 9); 153 | }, 154 | 155 | // Full year; e.g. 1980..2010 156 | Y: function () { return jsdate.getFullYear(); }, 157 | 158 | // Last two digits of year; 00..99 159 | y: function () { return (String(f.Y())).slice(-2); }, 160 | 161 | /* Time */ 162 | // am or pm 163 | a: function () { return jsdate.getHours() > 11 ? 'pm' : 'am'; }, 164 | 165 | // AM or PM 166 | A: function () { return f.a().toUpperCase(); }, 167 | 168 | // Swatch Internet time; 000..999 169 | B: function () { 170 | var unixTime = jsdate.getTime() / 1000; 171 | var secondsPassedToday = unixTime % 86400 + 3600; // since it's based off of UTC+1 172 | if (secondsPassedToday < 0) { secondsPassedToday += 86400; } 173 | var beats = ((secondsPassedToday) / 86.4) % 1000; 174 | if (unixTime < 0) { 175 | return Math.ceil(beats); 176 | } 177 | return Math.floor(beats); 178 | }, 179 | 180 | // 12-Hours; 1..12 181 | g: function () { return f.G() % 12 || 12; }, 182 | 183 | // 24-Hours; 0..23 184 | G: function () { return jsdate.getHours(); }, 185 | 186 | // 12-Hours w/leading 0; 01..12 187 | h: function () { return humanize.pad(f.g(), 2, '0'); }, 188 | 189 | // 24-Hours w/leading 0; 00..23 190 | H: function () { return humanize.pad(f.G(), 2, '0'); }, 191 | 192 | // Minutes w/leading 0; 00..59 193 | i: function () { return humanize.pad(jsdate.getMinutes(), 2, '0'); }, 194 | 195 | // Seconds w/leading 0; 00..59 196 | s: function () { return humanize.pad(jsdate.getSeconds(), 2, '0'); }, 197 | 198 | // Microseconds; 000000-999000 199 | u: function () { return humanize.pad(jsdate.getMilliseconds() * 1000, 6, '0'); }, 200 | 201 | // Whether or not the date is in daylight savings time 202 | /* 203 | I: function () { 204 | // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC. 205 | // If they are not equal, then DST is observed. 206 | var Y = f.Y(); 207 | return 0 + ((new Date(Y, 0) - Date.UTC(Y, 0)) !== (new Date(Y, 6) - Date.UTC(Y, 6))); 208 | }, 209 | */ 210 | 211 | // Difference to GMT in hour format; e.g. +0200 212 | O: function () { 213 | var tzo = jsdate.getTimezoneOffset(); 214 | var tzoNum = Math.abs(tzo); 215 | return (tzo > 0 ? '-' : '+') + humanize.pad(Math.floor(tzoNum / 60) * 100 + tzoNum % 60, 4, '0'); 216 | }, 217 | 218 | // Difference to GMT w/colon; e.g. +02:00 219 | P: function () { 220 | var O = f.O(); 221 | return (O.substr(0, 3) + ':' + O.substr(3, 2)); 222 | }, 223 | 224 | // Timezone offset in seconds (-43200..50400) 225 | Z: function () { return -jsdate.getTimezoneOffset() * 60; }, 226 | 227 | // Full Date/Time, ISO-8601 date 228 | c: function () { return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); }, 229 | 230 | // RFC 2822 231 | r: function () { return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); }, 232 | 233 | // Seconds since UNIX epoch 234 | U: function () { return jsdate.getTime() / 1000 || 0; } 235 | }; 236 | 237 | return format.replace(formatChr, formatChrCb); 238 | }; 239 | 240 | 241 | /** 242 | * format number by adding thousands separaters and significant digits while rounding 243 | */ 244 | humanize.numberFormat = function(number, decimals, decPoint, thousandsSep) { 245 | decimals = isNaN(decimals) ? 2 : Math.abs(decimals); 246 | decPoint = (decPoint === undefined) ? '.' : decPoint; 247 | thousandsSep = (thousandsSep === undefined) ? ',' : thousandsSep; 248 | 249 | var sign = number < 0 ? '-' : ''; 250 | number = Math.abs(+number || 0); 251 | 252 | var intPart = parseInt(number.toFixed(decimals), 10) + ''; 253 | var j = intPart.length > 3 ? intPart.length % 3 : 0; 254 | 255 | return sign + (j ? intPart.substr(0, j) + thousandsSep : '') + intPart.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep) + (decimals ? decPoint + Math.abs(number - intPart).toFixed(decimals).slice(2) : ''); 256 | }; 257 | 258 | 259 | /** 260 | * For dates that are the current day or within one day, return 'today', 'tomorrow' or 'yesterday', as appropriate. 261 | * Otherwise, format the date using the passed in format string. 262 | * 263 | * Examples (when 'today' is 17 Feb 2007): 264 | * 16 Feb 2007 becomes yesterday. 265 | * 17 Feb 2007 becomes today. 266 | * 18 Feb 2007 becomes tomorrow. 267 | * Any other day is formatted according to given argument or the DATE_FORMAT setting if no argument is given. 268 | */ 269 | humanize.naturalDay = function(timestamp, format) { 270 | timestamp = (timestamp === undefined) ? humanize.time() : timestamp; 271 | format = (format === undefined) ? 'Y-m-d' : format; 272 | 273 | var oneDay = 86400; 274 | var d = new Date(); 275 | var today = (new Date(d.getFullYear(), d.getMonth(), d.getDate())).getTime() / 1000; 276 | 277 | if (timestamp < today && timestamp >= today - oneDay) { 278 | return 'yesterday'; 279 | } else if (timestamp >= today && timestamp < today + oneDay) { 280 | return 'today'; 281 | } else if (timestamp >= today + oneDay && timestamp < today + 2 * oneDay) { 282 | return 'tomorrow'; 283 | } 284 | 285 | return humanize.date(format, timestamp); 286 | }; 287 | 288 | /** 289 | * returns a string representing how many seconds, minutes or hours ago it was or will be in the future 290 | * Will always return a relative time, most granular of seconds to least granular of years. See unit tests for more details 291 | */ 292 | humanize.relativeTime = function(timestamp) { 293 | timestamp = (timestamp === undefined) ? humanize.time() : timestamp; 294 | 295 | var currTime = humanize.time(); 296 | var timeDiff = currTime - timestamp; 297 | 298 | // within 2 seconds 299 | if (timeDiff < 2 && timeDiff > -2) { 300 | return (timeDiff >= 0 ? 'just ' : '') + 'now'; 301 | } 302 | 303 | // within a minute 304 | if (timeDiff < 60 && timeDiff > -60) { 305 | return (timeDiff >= 0 ? Math.floor(timeDiff) + ' seconds ago' : 'in ' + Math.floor(-timeDiff) + ' seconds'); 306 | } 307 | 308 | // within 2 minutes 309 | if (timeDiff < 120 && timeDiff > -120) { 310 | return (timeDiff >= 0 ? 'about a minute ago' : 'in about a minute'); 311 | } 312 | 313 | // within an hour 314 | if (timeDiff < 3600 && timeDiff > -3600) { 315 | return (timeDiff >= 0 ? Math.floor(timeDiff / 60) + ' minutes ago' : 'in ' + Math.floor(-timeDiff / 60) + ' minutes'); 316 | } 317 | 318 | // within 2 hours 319 | if (timeDiff < 7200 && timeDiff > -7200) { 320 | return (timeDiff >= 0 ? 'about an hour ago' : 'in about an hour'); 321 | } 322 | 323 | // within 24 hours 324 | if (timeDiff < 86400 && timeDiff > -86400) { 325 | return (timeDiff >= 0 ? Math.floor(timeDiff / 3600) + ' hours ago' : 'in ' + Math.floor(-timeDiff / 3600) + ' hours'); 326 | } 327 | 328 | // within 2 days 329 | var days2 = 2 * 86400; 330 | if (timeDiff < days2 && timeDiff > -days2) { 331 | return (timeDiff >= 0 ? '1 day ago' : 'in 1 day'); 332 | } 333 | 334 | // within 29 days 335 | var days29 = 29 * 86400; 336 | if (timeDiff < days29 && timeDiff > -days29) { 337 | return (timeDiff >= 0 ? Math.floor(timeDiff / 86400) + ' days ago' : 'in ' + Math.floor(-timeDiff / 86400) + ' days'); 338 | } 339 | 340 | // within 60 days 341 | var days60 = 60 * 86400; 342 | if (timeDiff < days60 && timeDiff > -days60) { 343 | return (timeDiff >= 0 ? 'about a month ago' : 'in about a month'); 344 | } 345 | 346 | var currTimeYears = parseInt(humanize.date('Y', currTime), 10); 347 | var timestampYears = parseInt(humanize.date('Y', timestamp), 10); 348 | var currTimeMonths = currTimeYears * 12 + parseInt(humanize.date('n', currTime), 10); 349 | var timestampMonths = timestampYears * 12 + parseInt(humanize.date('n', timestamp), 10); 350 | 351 | // within a year 352 | var monthDiff = currTimeMonths - timestampMonths; 353 | if (monthDiff < 12 && monthDiff > -12) { 354 | return (monthDiff >= 0 ? monthDiff + ' months ago' : 'in ' + (-monthDiff) + ' months'); 355 | } 356 | 357 | var yearDiff = currTimeYears - timestampYears; 358 | if (yearDiff < 2 && yearDiff > -2) { 359 | return (yearDiff >= 0 ? 'a year ago' : 'in a year'); 360 | } 361 | 362 | return (yearDiff >= 0 ? yearDiff + ' years ago' : 'in ' + (-yearDiff) + ' years'); 363 | }; 364 | 365 | /** 366 | * Converts an integer to its ordinal as a string. 367 | * 368 | * 1 becomes 1st 369 | * 2 becomes 2nd 370 | * 3 becomes 3rd etc 371 | */ 372 | humanize.ordinal = function(number) { 373 | number = parseInt(number, 10); 374 | number = isNaN(number) ? 0 : number; 375 | var sign = number < 0 ? '-' : ''; 376 | number = Math.abs(number); 377 | var tens = number % 100; 378 | 379 | return sign + number + (tens > 4 && tens < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[number % 10] || 'th'); 380 | }; 381 | 382 | /** 383 | * Formats the value like a 'human-readable' file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc). 384 | * 385 | * For example: 386 | * If value is 123456789, the output would be 117.7 MB. 387 | */ 388 | humanize.filesize = function(filesize, kilo, decimals, decPoint, thousandsSep, suffixSep) { 389 | kilo = (kilo === undefined) ? 1024 : kilo; 390 | if (filesize <= 0) { return '0 bytes'; } 391 | if (filesize < kilo && decimals === undefined) { decimals = 0; } 392 | if (suffixSep === undefined) { suffixSep = ' '; } 393 | return humanize.intword(filesize, ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'], kilo, decimals, decPoint, thousandsSep, suffixSep); 394 | }; 395 | 396 | /** 397 | * Formats the value like a 'human-readable' number (i.e. '13 K', '4.1 M', '102', etc). 398 | * 399 | * For example: 400 | * If value is 123456789, the output would be 117.7 M. 401 | */ 402 | humanize.intword = function(number, units, kilo, decimals, decPoint, thousandsSep, suffixSep) { 403 | var humanized, unit; 404 | 405 | units = units || ['', 'K', 'M', 'B', 'T'], 406 | unit = units.length - 1, 407 | kilo = kilo || 1000, 408 | decimals = isNaN(decimals) ? 2 : Math.abs(decimals), 409 | decPoint = decPoint || '.', 410 | thousandsSep = thousandsSep || ',', 411 | suffixSep = suffixSep || ''; 412 | 413 | for (var i=0; i < units.length; i++) { 414 | if (number < Math.pow(kilo, i+1)) { 415 | unit = i; 416 | break; 417 | } 418 | } 419 | humanized = number / Math.pow(kilo, unit); 420 | 421 | var suffix = units[unit] ? suffixSep + units[unit] : ''; 422 | return humanize.numberFormat(humanized, decimals, decPoint, thousandsSep) + suffix; 423 | }; 424 | 425 | /** 426 | * Replaces line breaks in plain text with appropriate HTML 427 | * A single newline becomes an HTML line break (
) and a new line followed by a blank line becomes a paragraph break (

). 428 | * 429 | * For example: 430 | * If value is Joel\nis a\n\nslug, the output will be

Joel
is a

slug

431 | */ 432 | humanize.linebreaks = function(str) { 433 | // remove beginning and ending newlines 434 | str = str.replace(/^([\n|\r]*)/, ''); 435 | str = str.replace(/([\n|\r]*)$/, ''); 436 | 437 | // normalize all to \n 438 | str = str.replace(/(\r\n|\n|\r)/g, "\n"); 439 | 440 | // any consecutive new lines more than 2 gets turned into p tags 441 | str = str.replace(/(\n{2,})/g, '

'); 442 | 443 | // any that are singletons get turned into br 444 | str = str.replace(/\n/g, '
'); 445 | return '

' + str + '

'; 446 | }; 447 | 448 | /** 449 | * Converts all newlines in a piece of plain text to HTML line breaks (
). 450 | */ 451 | humanize.nl2br = function(str) { 452 | return str.replace(/(\r\n|\n|\r)/g, '
'); 453 | }; 454 | 455 | /** 456 | * Truncates a string if it is longer than the specified number of characters. 457 | * Truncated strings will end with a translatable ellipsis sequence ('…'). 458 | */ 459 | humanize.truncatechars = function(string, length) { 460 | if (string.length <= length) { return string; } 461 | return string.substr(0, length) + '…'; 462 | }; 463 | 464 | /** 465 | * Truncates a string after a certain number of words. 466 | * Newlines within the string will be removed. 467 | */ 468 | humanize.truncatewords = function(string, numWords) { 469 | var words = string.split(' '); 470 | if (words.length < numWords) { return string; } 471 | return words.slice(0, numWords).join(' ') + '…'; 472 | }; 473 | 474 | }).call(this); 475 | -------------------------------------------------------------------------------- /specs/humanize.spec.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var humanize = require('../humanize'); 3 | process.env.TZ = 'America/Los_Angeles'; 4 | 5 | describe('humanize:', function() { 6 | 7 | describe('#pad', function() { 8 | it('should be able to pad on the left', function() { 9 | humanize.pad(123, 4, '0').should.equal('0123'); 10 | humanize.pad('abcd', 3, 'c').should.equal('abcd'); 11 | humanize.pad('cool', 7, 'Blah').should.equal('BBBcool'); 12 | }); 13 | 14 | it('should be able to pad on the right', function() { 15 | humanize.pad(123, 4, '0', 'right').should.equal('1230'); 16 | humanize.pad('abcd', 3, 'c', 'right').should.equal('abcd'); 17 | humanize.pad('cool', 7, 'Blah', 'right').should.equal('coolBBB'); 18 | }); 19 | }); 20 | 21 | 22 | describe('#time', function() { 23 | it('should be able to get the current time', function() { 24 | // I'm not sure how to make this better yet ... 25 | parseInt(humanize.time()).should.equal(parseInt(new Date().getTime() / 1000, 10)); 26 | }); 27 | }); 28 | 29 | describe('#date', function() { 30 | var timestamps = require('./dateData.js')().timestamps; 31 | 32 | it('should be able to accept timestamp, js date object, or nothing', function() { 33 | var timestamp = 514088627; 34 | var today = new Date(); 35 | humanize.date('Y-m-d').should.equal(today.getFullYear() + '-' + humanize.pad(today.getMonth() + 1, 2, '0') + '-' + humanize.pad(today.getDate(), 2, '0')); 36 | humanize.date('Y-m-d', timestamp).should.equal('1986-04-16'); 37 | humanize.date('Y-m-d', new Date(timestamp * 1000)).should.equal('1986-04-16'); 38 | }); 39 | 40 | it('should be able to print out escaped characters', function() { 41 | var timestamp = 514088627; 42 | var today = new Date(); 43 | humanize.date('Y-m-d\\Y\\z\\d').should.equal(today.getFullYear() + '-' + humanize.pad(today.getMonth() + 1, 2, '0') + '-' + humanize.pad(today.getDate(), 2, '0') + 'Yzd'); 44 | humanize.date('Y-m-d\\Y\\z\\d', timestamp).should.equal('1986-04-16Yzd'); 45 | humanize.date('Y-m-d\\Y\\z\\d', new Date(timestamp * 1000)).should.equal('1986-04-16Yzd'); 46 | }); 47 | 48 | it('should be able to replace correct information', function() { 49 | for (var timestamp in timestamps) { 50 | for (var dateVal in timestamps[timestamp]) { 51 | var info = 'timestamp: ' + timestamp + ' dateVal: ' + dateVal; 52 | humanize.date(dateVal, timestamp).should.eql(timestamps[timestamp][dateVal], info); 53 | } 54 | } 55 | }); 56 | 57 | }); 58 | 59 | 60 | describe('#numberFormat', function() { 61 | var number = 1234567.1234567; 62 | var negNumber = -1234567.1234567; 63 | it('should default using 2 decimals, "." as decimal point, "," as thousands separator', function() { 64 | humanize.numberFormat(number).should.equal('1,234,567.12'); 65 | }); 66 | 67 | it('should be able to deal with different number of decimals properly + rounding', function() { 68 | humanize.numberFormat(number, 0).should.equal('1,234,567'); 69 | humanize.numberFormat(number, 3).should.equal('1,234,567.123'); 70 | humanize.numberFormat(number, 4).should.equal('1,234,567.1235'); 71 | humanize.numberFormat(number, 5).should.equal('1,234,567.12346'); 72 | humanize.numberFormat(number, 6).should.equal('1,234,567.123457'); 73 | humanize.numberFormat(number, 7).should.equal('1,234,567.1234567'); 74 | humanize.numberFormat(number, 8).should.equal('1,234,567.12345670'); 75 | humanize.numberFormat(number, 9).should.equal('1,234,567.123456700'); 76 | 77 | humanize.numberFormat(negNumber, 0).should.equal('-1,234,567'); 78 | humanize.numberFormat(negNumber, 3).should.equal('-1,234,567.123'); 79 | humanize.numberFormat(negNumber, 4).should.equal('-1,234,567.1235'); 80 | humanize.numberFormat(negNumber, 5).should.equal('-1,234,567.12346'); 81 | humanize.numberFormat(negNumber, 6).should.equal('-1,234,567.123457'); 82 | humanize.numberFormat(negNumber, 7).should.equal('-1,234,567.1234567'); 83 | humanize.numberFormat(negNumber, 8).should.equal('-1,234,567.12345670'); 84 | humanize.numberFormat(negNumber, 9).should.equal('-1,234,567.123456700'); 85 | }); 86 | 87 | it('should be able to deal with negative decimals as if they were positive', function() { 88 | humanize.numberFormat(number, -3).should.equal(humanize.numberFormat(number, 3)); 89 | }); 90 | 91 | it('should be able to change the decimal point to a different string', function() { 92 | humanize.numberFormat(number, 3, 'P').should.equal('1,234,567P123'); 93 | humanize.numberFormat(number, 3, ',').should.equal('1,234,567,123'); 94 | humanize.numberFormat(number, 3, 'what?').should.equal('1,234,567what?123'); 95 | }); 96 | 97 | it('should be able to change the thousands separator to a different string', function() { 98 | humanize.numberFormat(number, 3, '.', '.').should.equal('1.234.567.123'); 99 | humanize.numberFormat(number, 3, ',', '.').should.equal('1.234.567,123'); 100 | humanize.numberFormat(number, 3, '.', 'huh?').should.equal('1huh?234huh?567.123'); 101 | }); 102 | }); 103 | 104 | describe('#naturalDay', function() { 105 | var d = new Date(); 106 | var today = (new Date(d.getFullYear(), d.getMonth(), d.getDate())).getTime() / 1000; 107 | 108 | it('should return today when using today', function() { 109 | humanize.naturalDay(today).should.equal('today'); 110 | humanize.naturalDay(today + 43200).should.equal('today'); 111 | humanize.naturalDay(today + 86399).should.equal('today'); 112 | }); 113 | 114 | it('should return yesterday when using yesterday', function() { 115 | humanize.naturalDay(today - 1).should.equal('yesterday'); 116 | humanize.naturalDay(today - 43200).should.equal('yesterday'); 117 | humanize.naturalDay(today - 86400).should.equal('yesterday'); 118 | }); 119 | 120 | it('should return tomorrow when using tomorrow', function() { 121 | humanize.naturalDay(today + 86400).should.equal('tomorrow'); 122 | humanize.naturalDay(today + 86400 + 43200).should.equal('tomorrow'); 123 | humanize.naturalDay(today + 86400 + 86399).should.equal('tomorrow'); 124 | }); 125 | 126 | it('should return date when before yesterday with default formatting', function() { 127 | humanize.naturalDay(today - 86401).should.equal(humanize.date('Y-m-d', today - 86401)); 128 | humanize.naturalDay(today - 86411).should.equal(humanize.date('Y-m-d', today - 86411)); 129 | humanize.naturalDay(today - (2 * 86401)).should.equal(humanize.date('Y-m-d', today - (2 * 86401))); 130 | }); 131 | 132 | it('should return date when before yesterday with custom formatting', function() { 133 | humanize.naturalDay(today - 86401, 'h:i:s').should.equal(humanize.date('h:i:s', today - 86401)); 134 | humanize.naturalDay(today - 86401, 'l F j, Y h:i:s').should.equal(humanize.date('l F j, Y h:i:s', today - 86401)); 135 | }); 136 | 137 | it('should return date when after tomorrow', function() { 138 | humanize.naturalDay(today + 86400 + 86401).should.equal(humanize.date('Y-m-d', today + 86400 + 86401)); 139 | humanize.naturalDay(today + 86400 + 86411).should.equal(humanize.date('Y-m-d', today + 86400 + 86411)); 140 | humanize.naturalDay(today + (2 * 86401)).should.equal(humanize.date('Y-m-d', today + (2 * 86401))); 141 | }); 142 | 143 | it('should return date when before tomorrow with custom formatting', function() { 144 | humanize.naturalDay(today + 86400 + 86401, 'h:i:s').should.equal(humanize.date('h:i:s', today + 86400 + 86401)); 145 | humanize.naturalDay(today + 86400 + 86401, 'l F j, Y h:i:s').should.equal(humanize.date('l F j, Y h:i:s', today + 86400 + 86401)); 146 | }); 147 | }); 148 | 149 | describe('#relativeTime', function() { 150 | it('should return just now for anything within 2 seconds', function() { 151 | humanize.relativeTime(humanize.time() - 1).should.equal('just now'); 152 | humanize.relativeTime(humanize.time() - .5).should.equal('just now'); 153 | humanize.relativeTime(humanize.time()).should.equal('just now'); 154 | humanize.relativeTime(humanize.time() + .5).should.equal('now'); 155 | humanize.relativeTime(humanize.time() + 1).should.equal('now'); 156 | }); 157 | 158 | it('should return (in) X seconds (ago) for anything between 2 seconds and 59 seconds inclusive', function() { 159 | humanize.relativeTime(humanize.time() - 59).should.equal('59 seconds ago'); 160 | humanize.relativeTime(humanize.time() - 37).should.equal('37 seconds ago'); 161 | humanize.relativeTime(humanize.time() - 37.3).should.equal('37 seconds ago'); 162 | humanize.relativeTime(humanize.time() - 2).should.equal('2 seconds ago'); 163 | humanize.relativeTime(humanize.time() + 2).should.equal('in 2 seconds'); 164 | humanize.relativeTime(humanize.time() + 22).should.equal('in 22 seconds'); 165 | humanize.relativeTime(humanize.time() + 22.7).should.equal('in 22 seconds'); 166 | humanize.relativeTime(humanize.time() + 59).should.equal('in 59 seconds'); 167 | }); 168 | 169 | it('should return (in) about a minute (ago) for anything between 1 minute (inclusive) and 2 minutes (exclusive)', function() { 170 | humanize.relativeTime(humanize.time() - 119).should.equal('about a minute ago'); 171 | humanize.relativeTime(humanize.time() - 73).should.equal('about a minute ago'); 172 | humanize.relativeTime(humanize.time() - 60).should.equal('about a minute ago'); 173 | humanize.relativeTime(humanize.time() + 60).should.equal('in about a minute'); 174 | humanize.relativeTime(humanize.time() + 90).should.equal('in about a minute'); 175 | humanize.relativeTime(humanize.time() + 119).should.equal('in about a minute'); 176 | }); 177 | 178 | it('should return (in) X minutes (ago) for anything between 2 minutes (inclusive) and 60 minutes (exclusive)', function() { 179 | humanize.relativeTime(humanize.time() - 59*60 - 59).should.equal('59 minutes ago'); 180 | humanize.relativeTime(humanize.time() - 59*60 - 10).should.equal('59 minutes ago'); 181 | humanize.relativeTime(humanize.time() - 33*60 - 17).should.equal('33 minutes ago'); 182 | humanize.relativeTime(humanize.time() - 33*60 - 35).should.equal('33 minutes ago'); 183 | humanize.relativeTime(humanize.time() - 120).should.equal('2 minutes ago'); 184 | humanize.relativeTime(humanize.time() + 120).should.equal('in 2 minutes'); 185 | humanize.relativeTime(humanize.time() + 24*60 + 17).should.equal('in 24 minutes'); 186 | humanize.relativeTime(humanize.time() + 47*60 + 35).should.equal('in 47 minutes'); 187 | humanize.relativeTime(humanize.time() + 59*60 + 16).should.equal('in 59 minutes'); 188 | humanize.relativeTime(humanize.time() + 59*60 + 59).should.equal('in 59 minutes'); 189 | }); 190 | 191 | it('should return (in) about an hour (ago) for anything between 1 hour (inclusive) and 2 hours (exclusive)', function() { 192 | humanize.relativeTime(humanize.time() - 7199).should.equal('about an hour ago'); 193 | humanize.relativeTime(humanize.time() - 3601).should.equal('about an hour ago'); 194 | humanize.relativeTime(humanize.time() - 3600).should.equal('about an hour ago'); 195 | humanize.relativeTime(humanize.time() + 3600).should.equal('in about an hour'); 196 | humanize.relativeTime(humanize.time() + 5974).should.equal('in about an hour'); 197 | humanize.relativeTime(humanize.time() + 7199).should.equal('in about an hour'); 198 | }); 199 | 200 | it('should return (in) X hours (ago) for anything between 2 hours (inclusive) and 24 hours (exclusive)', function() { 201 | humanize.relativeTime(humanize.time() - 86399).should.equal('23 hours ago'); 202 | humanize.relativeTime(humanize.time() - (3*3600 + 56)).should.equal('3 hours ago'); 203 | humanize.relativeTime(humanize.time() - (15*3600 + 3599)).should.equal('15 hours ago'); 204 | humanize.relativeTime(humanize.time() - 7200).should.equal('2 hours ago'); 205 | humanize.relativeTime(humanize.time() + 7200).should.equal('in 2 hours'); 206 | humanize.relativeTime(humanize.time() + (10*3600 + 997)).should.equal('in 10 hours'); 207 | humanize.relativeTime(humanize.time() + (15*3600 + 3599)).should.equal('in 15 hours'); 208 | humanize.relativeTime(humanize.time() + 86399).should.equal('in 23 hours'); 209 | }); 210 | 211 | it('should return (in) X day(s) (ago) for anything between 1 day (inclusive) and 29 days (exclusive)', function() { 212 | humanize.relativeTime(humanize.time() - (29*86400 - 1)).should.equal('28 days ago'); 213 | humanize.relativeTime(humanize.time() - (2*86400)).should.equal('2 days ago'); 214 | humanize.relativeTime(humanize.time() - (2*86400 - 1)).should.equal('1 day ago'); 215 | humanize.relativeTime(humanize.time() - 86400).should.equal('1 day ago'); 216 | humanize.relativeTime(humanize.time() + 86400).should.equal('in 1 day'); 217 | humanize.relativeTime(humanize.time() + (2*86400)).should.equal('in 2 days'); 218 | humanize.relativeTime(humanize.time() + (29*86400 - 1)).should.equal('in 28 days'); 219 | }); 220 | 221 | it('should return (in) about a month (ago) for anything between 28 days (inclusive) to 60 days (exclusive)', function() { 222 | humanize.relativeTime(humanize.time() - (60*86400 - 1)).should.equal('about a month ago'); 223 | humanize.relativeTime(humanize.time() - (29*86400)).should.equal('about a month ago'); 224 | humanize.relativeTime(humanize.time() + (29*86400)).should.equal('in about a month'); 225 | humanize.relativeTime(humanize.time() + (60*86400 - 1)).should.equal('in about a month'); 226 | }); 227 | 228 | it('should return (in) X months (ago) using month arithmetic', function() { 229 | humanize.relativeTime(humanize.time() - (60*86400)).should.equal('2 months ago'); 230 | humanize.relativeTime(humanize.time() + (60*86400)).should.equal('in 2 months'); 231 | 232 | var d = new Date(); 233 | var monthsAgo4 = (new Date(d.getFullYear(), d.getMonth() - 4, d.getDate())).getTime() / 1000; 234 | humanize.relativeTime(monthsAgo4).should.equal('4 months ago'); 235 | 236 | var monthsFuture4 = (new Date(d.getFullYear(), d.getMonth() + 4, d.getDate())).getTime() / 1000; 237 | humanize.relativeTime(monthsFuture4).should.equal('in 4 months'); 238 | 239 | var monthsAgo11 = (new Date(d.getFullYear(), d.getMonth() - 11, d.getDate())).getTime() / 1000; 240 | humanize.relativeTime(monthsAgo11).should.equal('11 months ago'); 241 | 242 | var monthsFuture11 = (new Date(d.getFullYear(), d.getMonth() + 11, d.getDate())).getTime() / 1000; 243 | humanize.relativeTime(monthsFuture11).should.equal('in 11 months'); 244 | }); 245 | 246 | it('should return (in) X year(s) (ago) for anything over a year via year arithmetic', function() { 247 | var d = new Date(); 248 | var yearsAgo1 = (new Date(d.getFullYear() - 1, d.getMonth(), d.getDate())).getTime() / 1000; 249 | humanize.relativeTime(yearsAgo1).should.equal('a year ago'); 250 | 251 | var yearsFuture1 = (new Date(d.getFullYear() + 1, d.getMonth(), d.getDate())).getTime() / 1000; 252 | humanize.relativeTime(yearsFuture1).should.equal('in a year'); 253 | 254 | var june1 = (new Date(d.getFullYear(), 5, 1)); 255 | 256 | var yearsAgo2 = (new Date(june1.getFullYear() - 2, june1.getMonth() + 6, june1.getDate() + 19)).getTime() / 1000; 257 | humanize.relativeTime(yearsAgo2).should.equal('2 years ago'); 258 | 259 | var wrapToYearsAgo1 = (new Date(june1.getFullYear() - 2, june1.getMonth() + 7, june1.getDate() + 19)).getTime() / 1000; 260 | humanize.relativeTime(wrapToYearsAgo1).should.equal('a year ago'); 261 | 262 | var yearsFuture2 = (new Date(june1.getFullYear() + 2, june1.getMonth() + 6, june1.getDate() + 19)).getTime() / 1000; 263 | humanize.relativeTime(yearsFuture2).should.equal('in 2 years'); 264 | 265 | var wrapToYearsFuture1 = (new Date(june1.getFullYear() + 2, june1.getMonth() - 7, june1.getDate() + 19)).getTime() / 1000; 266 | humanize.relativeTime(wrapToYearsFuture1).should.equal('in a year'); 267 | 268 | }); 269 | 270 | }); 271 | 272 | describe('#ordinal', function() { 273 | it('should be able to return the correct ordinal string', function() { 274 | var tests = { 275 | 0: '0th', 276 | 1: '1st', 277 | 2: '2nd', 278 | 3: '3rd', 279 | 4: '4th', 280 | 5: '5th', 281 | 11: '11th', 282 | 12: '12th', 283 | 13: '13th', 284 | 21: '21st', 285 | 31: '31st', 286 | 32: '32nd', 287 | 43: '43rd', 288 | '87 Street': '87th', 289 | '223 APT 23': '223rd', 290 | 'APT': '0th', 291 | '-1': '-1st', 292 | '-2': '-2nd', 293 | '-3': '-3rd', 294 | 112: '112th' 295 | }; 296 | 297 | for (var num in tests) { 298 | humanize.ordinal(num).should.equal(tests[num]); 299 | } 300 | }); 301 | }); 302 | 303 | 304 | describe('#filesize', function() { 305 | it('should be able to use the defaults properly', function() { 306 | humanize.filesize(12).should.equal('12 bytes'); 307 | humanize.filesize(1021).should.equal('1,021 bytes'); 308 | humanize.filesize(1024).should.equal('1.00 KB'); 309 | 310 | humanize.filesize(Math.pow(1024, 2)).should.equal('1.00 MB'); 311 | humanize.filesize(Math.pow(1024, 3)).should.equal('1.00 GB'); 312 | humanize.filesize(Math.pow(1024, 4)).should.equal('1.00 TB'); 313 | humanize.filesize(Math.pow(1024, 5)).should.equal('1.00 PB'); 314 | humanize.filesize(Math.pow(1024, 6)).should.equal('1,024.00 PB'); 315 | humanize.filesize(1234567890).should.equal('1.15 GB'); 316 | }); 317 | 318 | it('should be able to change kilo to a different value', function() { 319 | humanize.filesize(12, 1000).should.equal('12 bytes'); 320 | humanize.filesize(1021, 1000).should.equal('1.02 KB'); 321 | humanize.filesize(1024, 1000).should.equal('1.02 KB'); 322 | humanize.filesize(Math.pow(1024, 2), 1000).should.equal('1.05 MB'); 323 | humanize.filesize(Math.pow(1024, 3), 1000).should.equal('1.07 GB'); 324 | humanize.filesize(Math.pow(1024, 4), 1000).should.equal('1.10 TB'); 325 | humanize.filesize(Math.pow(1024, 5), 1000).should.equal('1.13 PB'); 326 | humanize.filesize(Math.pow(1024, 6), 1000).should.equal('1,152.92 PB'); 327 | humanize.filesize(1234567890, 1000).should.equal('1.23 GB'); 328 | }); 329 | }); 330 | 331 | 332 | describe('#intword', function() { 333 | it('should be able to use the defaults properly', function() { 334 | humanize.intword(12).should.equal('12.00'); 335 | humanize.intword(999).should.equal('999.00'); 336 | humanize.intword(1001).should.equal('1.00K'); 337 | 338 | humanize.intword(Math.pow(1000, 2)).should.equal('1.00M'); 339 | humanize.intword(Math.pow(1000, 3)).should.equal('1.00B'); 340 | humanize.intword(Math.pow(1000, 4)).should.equal('1.00T'); 341 | humanize.intword(1234567890).should.equal('1.23B'); 342 | }); 343 | 344 | it('should be able to change units or kilo to a different value', function() { 345 | var units = ['ones', 'thousands', 'millions', 'billions', 'trillions']; 346 | humanize.intword(12, units, 1000, 0, '.', ',', ' ').should.equal('12 ones'); 347 | humanize.intword(999, units, 1000, 0, '.', ',', ' ').should.equal('999 ones'); 348 | humanize.intword(1024, units, 1000, 0, '.', ',', ' ').should.equal('1 thousands'); 349 | humanize.intword(Math.pow(1000, 2), units, 1000, 0, '.', ',', ' ').should.equal('1 millions'); 350 | humanize.intword(Math.pow(1000, 3), units, 1000, 0, '.', ',', ' ').should.equal('1 billions'); 351 | humanize.intword(Math.pow(1000, 4), units, 1000, 0, '.', ',', ' ').should.equal('1 trillions'); 352 | humanize.intword(1234567890, units, 1000, 0, '.', ',', ' ').should.equal('1 billions'); 353 | }); 354 | }); 355 | 356 | 357 | describe('#linebreaks', function() { 358 | it('should wrap the string with

tags', function() { 359 | humanize.linebreaks('').should.equal('

'); 360 | }); 361 | 362 | it('should remove new lines at beginning and end', function() { 363 | humanize.linebreaks("Foo\n\nBar\n\n\n").should.equal('

Foo

Bar

'); 364 | humanize.linebreaks("\n\r\n\rFoo\n\nBar").should.equal('

Foo

Bar

'); 365 | }); 366 | 367 | it('should change all new lines into
tags', function() { 368 | humanize.linebreaks("Foo\nBar").should.equal('

Foo
Bar

'); 369 | humanize.linebreaks("Foo\nBar\r\nBlah").should.equal('

Foo
Bar
Blah

'); 370 | }); 371 | 372 | it('should change all multi-new lines into

tags', function() { 373 | humanize.linebreaks("Foo\n\nBar").should.equal('

Foo

Bar

'); 374 | humanize.linebreaks("Foo\n\n\nBar").should.equal('

Foo

Bar

'); 375 | humanize.linebreaks("Foo\n\n\r\nBar").should.equal('

Foo

Bar

'); 376 | humanize.linebreaks("Foo\n\n\r\n\rBar").should.equal('

Foo

Bar

'); 377 | }); 378 | }); 379 | 380 | describe('#nl2br', function() { 381 | it('should change any type of new line into a
', function() { 382 | humanize.nl2br('').should.equal(''); 383 | humanize.nl2br("\n").should.equal('
'); 384 | humanize.nl2br("\r").should.equal('
'); 385 | humanize.nl2br("\r\n").should.equal('
'); 386 | humanize.nl2br("Foo\nBar").should.equal('Foo
Bar'); 387 | humanize.nl2br("\r\nFoo\nBar\n").should.equal('
Foo
Bar
'); 388 | humanize.nl2br("\r\r\n\nFoo\nBar\n\n\r\n\r").should.equal('


Foo
Bar



'); 389 | }); 390 | }); 391 | 392 | describe('#truncatechars', function() { 393 | it('should be able to truncate characters properly', function() { 394 | humanize.truncatechars('foobar', 0).should.equal('…'); 395 | humanize.truncatechars('foobar', 1).should.equal('f…'); 396 | humanize.truncatechars('foobar', 2).should.equal('fo…'); 397 | humanize.truncatechars('foobar', 3).should.equal('foo…'); 398 | humanize.truncatechars('foobar', 4).should.equal('foob…'); 399 | }); 400 | }); 401 | 402 | describe('#truncatewords', function() { 403 | it('should be able to truncate words properly', function() { 404 | humanize.truncatewords('a b c d e', 0).should.equal('…'); 405 | humanize.truncatewords('a b c d e', 1).should.equal('a…'); 406 | humanize.truncatewords('a b c d e', 2).should.equal('a b…'); 407 | humanize.truncatewords('a b c d e', 3).should.equal('a b c…'); 408 | humanize.truncatewords('a b c d e', 4).should.equal('a b c d…'); 409 | }); 410 | }); 411 | 412 | 413 | 414 | }); 415 | --------------------------------------------------------------------------------