Skip to content
This repository

date/strftime #83

Merged
merged 7 commits into from about 1 year ago

3 participants

Miller Medeiros Mathias Paumgarten Conrad Zimmerman
Miller Medeiros
Owner

started date/strftime implementation.

following the tokens present on python, ruby and C

still missing some stuff but basic features are working.. I also added an internal date/i18n_ module to allow setting the locale globally but I guess we need another option to override the default locale as well (maybe 3rd argument).

see #78

tests/spec/date/spec-strftime.js
((121 lines not shown))
  121 + }
  122 + }, '%z') ).toBe('-0330');
  123 + expect( strftime({
  124 + getTimezoneOffset : function(){
  125 + return -210;
  126 + }
  127 + }, '%z') ).toBe('+0330');
  128 + });
  129 +
  130 + it('should support escaping %%', function () {
  131 + expect( strftime(date, '%%') ).toEqual('%');
  132 + });
  133 +
  134 + it('should support multiple tokens at once', function () {
  135 + // iso8601
  136 + expect( strftime(date, '%Y-%m-%dT%H:%M:%S%z') ).toEqual( '2013-04-08T09:02:04-0300' );
1
Miller Medeiros Owner

ops, we need to remove the %z otherwise test will fail if timezoneOffset is different..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
tests/spec/date/spec-strftime.js
((68 lines not shown))
  68 + it('should return zero-padded minutes for %M', function () {
  69 + expect( strftime(date, '%M') ).toBe('02');
  70 + expect( strftime(date_2, '%M') ).toBe('22');
  71 + });
  72 +
  73 + it('should return newline char for %n', function () {
  74 + expect( strftime(date, '%n') ).toBe('\n');
  75 + });
  76 +
  77 + it('should return am/pm for %p', function () {
  78 + expect( strftime(date, '%p') ).toBe('am');
  79 + expect( strftime(date_2, '%p') ).toBe('pm');
  80 + });
  81 +
  82 + it('should return number of seconds since epoch for %s', function () {
  83 + expect( strftime(date, '%s') ).toBe('1365422524');
1
Miller Medeiros Owner

this will fail on travis server since timezone is different from my local machine.. we need to mock the date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Miller Medeiros
Owner

I'm wondering if we should split each case into its own function (ie. date/getTimezone, date/getCentury, date/toSeconds, date/weekDayName, date/monthNameAbbr, etc..)

It will probably make things easier to reuse and can make code cleaner in some cases (eg. while building a calendar you probably need weekDayName and monthName, strftime(date, '%A') is not very readable..)

Miller Medeiros
Owner

implementation is covering all the tokens from http://pubs.opengroup.org/onlinepubs/007908799/xsh/strftime.html besides %V (ISO 8601 week number)

I extracted all the formats that aren't one-liners into separate modules and kept the localization info as a separate file, that way we could potentially create multiple i18n files inside the date package.

strftime is really cryptic, I think we should really implement LDML date format after strftime is done.

Miller Medeiros
Owner

I'm almost merging this without the ISO week number support (%V) since I will probably get the implementation wrong (or take a long time to do it right) and I never needed it. This PR is already holding the v0.6 release for too long...

Anyone against an incomplete implementation of strftime landing on v0.6? (with the option to improve it later if needed)

/cc @conradz @MathiasPaumgarten @leocavalcante

Mathias Paumgarten
Collaborator

Sorry, I've been a little lazy on this PR since it's quite a junk of code to read through. I have no objections at all. A welcomed feature for sure, I was just keeping quiet since I never got around to check out the PR closely. Shame on me.

Anyways :+1: and looking forward to 0.6.0!

Conrad Zimmerman
Owner
conradz commented

I would be OK with an incomplete implementation, as long as the incomplete parts (parts implemented in the C strftime and not implemented in this one) are documented. Probably only a few people need the %V.

Miller Medeiros
Owner

I rewrote the whole commit history (so it makes more sense) and added docs to all the new methods. I also marked the %V with a <del> (which will render with a strikethrough on the docs) so it should be enough for now.

I think it's good to merge, planning to do it tomorrow and release v0.6 after that.

Mathias Paumgarten
Collaborator

I will add a de-DE version once it's merged, so I can pull and create e PR.

Miller Medeiros millermedeiros merged commit c2f4f42 into from
Miller Medeiros millermedeiros deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
13 _build/doc_template/assets_/css/main.css
@@ -287,11 +287,20 @@ sub {
287 287 ul,
288 288 ol {
289 289 margin-left: 0;
290   - padding: 0 0 0 40px;
  290 + padding: 0 0 0 3em;
  291 +}
  292 +
  293 +dl {
  294 + margin: 2em 1em;
  295 +}
  296 +
  297 +dt {
  298 + font-weight: bold;
  299 + margin-top: 0.5em;
291 300 }
292 301
293 302 dd {
294   - margin: 0 0 0 40px;
  303 + margin: 0 0 0 3em;
295 304 }
296 305
297 306 nav ul,
153 doc/date.md
Source Rendered
@@ -3,6 +3,20 @@
3 3 Date utilities.
4 4
5 5
  6 +## dayOfTheYear(date):Number
  7 +
  8 +How many days elapsed since begining of the year (following gregorian
  9 +calendar).
  10 +
  11 +```js
  12 +// Jan 1st
  13 +dayOfTheYear(new Date(2013, 0, 1)); // 1
  14 +// Dec 31th
  15 +dayOfTheYear(new Date(2013, 11, 31)); // 364
  16 +```
  17 +
  18 +
  19 +
6 20 ## diff(date1, date2, [unitName]):Number
7 21
8 22 Calculate the difference between dates (range).
@@ -103,6 +117,122 @@ startOf(date, 'hour'); // Apr 05 2013 11:00:00
103 117
104 118
105 119
  120 +## strftime(date, format, [l10n]):String
  121 +
  122 +Format date based on strftime format.
  123 +
  124 +Replaced tokens:
  125 +
  126 +<dl>
  127 +<dt>%a</dt><dd> locale's abbreviated weekday name.</dd>
  128 +<dt>%A</dt><dd> locale's full weekday name.</dd>
  129 +<dt>%b</dt><dd> locale's abbreviated month name.</dd>
  130 +<dt>%B</dt><dd> locale's full month name.</dd>
  131 +<dt>%c</dt><dd> locale's appropriate date and time representation.</dd>
  132 +<dt>%C</dt><dd> century number (the year divided by 100 and truncated
  133 +to an integer) as a decimal number [00..99].</dd>
  134 +<dt>%d</dt><dd> day of the month as a decimal number [01..31].</dd>
  135 +<dt>%D</dt><dd>same as %m/%d/%y.</dd>
  136 +<dt>%e</dt><dd> day of the month as a decimal number [1..31];
  137 +a single digit is preceded by a space.</dd>
  138 +<dt>%F</dt><dd>The ISO 8601 date format (%Y-%m-%d)</dd>
  139 +<dt>%h</dt><dd>same as %b.</dd>
  140 +<dt>%H</dt><dd> hour (24-hour clock) as a decimal number [00..23].</dd>
  141 +<dt>%I</dt><dd> hour (12-hour clock) as a decimal number [01..12].</dd>
  142 +<dt>%j</dt><dd> day of the year as a decimal number [001..366].</dd>
  143 +<dt>%L</dt><dd> zero-padded milliseconds [000..999]</dd>
  144 +<dt>%m</dt><dd> month as a decimal number [01..12].</dd>
  145 +<dt>%M</dt><dd> minute as a decimal number [00..59].</dd>
  146 +<dt>%n</dt><dd> newline character.</dd>
  147 +<dt>%p</dt><dd> locale's equivalent of either "am" or "pm"</dd>
  148 +<dt>%P</dt><dd> locale's equivalent of either "AM" or "PM"</dd>
  149 +<dt>%r</dt><dd> time in a.m. and
  150 +p.m. notation; in the POSIX locale this is equivalent to %I:%M:%S %p.</dd>
  151 +<dt>%R</dt><dd> time in 24 hour notation (%H:%M).</dd>
  152 +<dt>%s</dt><dd> seconds since Epoch (1970-01-01 00:00:00 UTC)</dd>
  153 +<dt>%S</dt><dd> second as a decimal number [00..60].</dd>
  154 +<dt>%t</dt><dd> tab character.</dd>
  155 +<dt>%T</dt><dd> time (%H:%M:%S).</dd>
  156 +<dt>%u</dt><dd> weekday as a decimal number [1..7], with 1 representing
  157 +Monday.</dd>
  158 +<dt>%U</dt><dd> week number of the year (Sunday as the first day of
  159 +the week) as a decimal number [00..53].</dd>
  160 +<del><dt>%V</dt><dd> week number of the year (Monday as the first day of the
  161 +week) as a decimal number [01..53]. If the week containing 1 January has
  162 +four or more days in the new year, then it is considered week 1. Otherwise,
  163 +it is the last week of the previous year, and the next week is week 1.</dd></del>
  164 +<dt>%w</dt><dd> weekday as a decimal number [0..6], with 0 representing
  165 +Sunday.</dd>
  166 +<dt>%W</dt><dd> week number of the year (Monday as the first day of
  167 +the week) as a decimal number [00..53]. All days in a new year preceding
  168 +the first Monday are considered to be in week 0.</dd>
  169 +<dt>%x</dt><dd> locale's appropriate date representation.</dd>
  170 +<dt>%X</dt><dd> locale's appropriate time representation.</dd>
  171 +<dt>%y</dt><dd> year without century as a decimal number [00..99].</dd>
  172 +<dt>%Y</dt><dd> year with century as a decimal number.</dd>
  173 +<dt>%Z</dt><dd> timezone name or abbreviation, or by no bytes
  174 +if no timezone information exists.</dd>
  175 +<dt>%%</dt><dd>is replaced by %.</dd>
  176 +</dl>
  177 +
  178 +```js
  179 +var date = new Date(2013, 3, 8, 9, 2, 4);
  180 +strftime(date, '%Y-%m-%d'); // "2013-04-08"
  181 +strftime(date, '%R'); // "09:02"
  182 +strftime(date, '%Y-%m-%dT%H:%M:%S%z'); // "2013-04-08T09:02:04+0000"
  183 +```
  184 +
  185 +You can also set a custom locale:
  186 +
  187 +```js
  188 +var ptBr = require('mout/date/i18n/pt-BR');
  189 +strftime(date, '%a, %d %b', ptBr); // 'Seg, 08 Abr'
  190 +strftime(date, '%A, %d %B', ptBr); // 'Segunda, 08 Abril'
  191 +```
  192 +
  193 +To set it globally:
  194 +
  195 +```js
  196 +require('mout/date/i18n_').set( customLocaleData );
  197 +```
  198 +
  199 +See [date/i18n](https://github.com/mout/mout/tree/master/src/date/i18n)
  200 +for localization examples.
  201 +
  202 +
  203 +
  204 +## timezoneAbbr(date):String
  205 +
  206 +Return timezone abbreviation or similar data.
  207 +
  208 +The result will vary based on the OS/browser since some environments doesn't
  209 +provide enough info about the current locale.
  210 +
  211 +```js
  212 +// IE 7-8
  213 +timezoneAbbr(new Date()); // "-0500"
  214 +// Chrome, FF, V8
  215 +timezoneAbbr(new Date()); // "EST"
  216 +```
  217 +
  218 +
  219 +
  220 +## timezoneOffset(date):String
  221 +
  222 +Return time zone as hour and minute offset from UTC (e.g. +0900).
  223 +
  224 +It's important to note that JavaScript Date object will use the system locale
  225 +info to determinate the [timezone
  226 +offset](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset)
  227 +and that daylight saving time affects the result.
  228 +
  229 +```js
  230 +// if system locale is EST
  231 +timezoneOffset(new Date()); // -0500
  232 +```
  233 +
  234 +
  235 +
106 236 ## totalDaysInMonth(fullYear, monthIndex):Number
107 237
108 238 Returns the amount of days in the month taking into consideration leap years
@@ -135,6 +265,29 @@ totalDaysInYear( new Date(2013, 0, 1) ); // 365
135 265 ```
136 266
137 267
  268 +
  269 +## weekOfTheYear(date, [firstDayOfWeek]):Number
  270 +
  271 +Returns how many weeks elapsed since start of the year (`0..53`).
  272 +
  273 +`firstDayOfWeek` can be `0` (Sunday) or `1` (Monday). By default weeks start at
  274 +Sunday.
  275 +
  276 +It will return `0` if `date` is before the first `firstDayOfWeek` of the year.
  277 +
  278 +```js
  279 +// Tue Jan 01 2013
  280 +weekOfTheYear( new Date(2013,0,1) ); // 0
  281 +// Wed Jan 09 2013
  282 +weekOfTheYear( new Date(2013,0,9) ); // 1
  283 +// Sun Jan 01 2012
  284 +weekOfTheYear( new Date(2012,0,1) ); // 1
  285 +// Mon Jan 09 2012
  286 +weekOfTheYear( new Date(2012,0,9) ); // 2
  287 +```
  288 +
  289 +
  290 +
138 291 -------------------------------------------------------------------------------
139 292
140 293 For more usage examples check specs inside `/tests` folder. Unit tests are the
19 doc/number.md
Source Rendered
@@ -109,21 +109,26 @@ console.log( MIN_INT ); // -2147483648
109 109 ```
110 110
111 111
112   -## pad(n, minLength):String
  112 +## pad(n, minLength[, char]):String
113 113
114 114 Add padding zeros if `n.length` < `minLength`.
115 115
116 116 ### Example:
117 117
118 118 ```js
119   -pad(1, 5); // 00001
120   -pad(12, 5); // 00012
121   -pad(123, 5); // 00123
122   -pad(1234, 5); // 01234
123   -pad(12345, 5); // 12345
124   -pad(123456, 5); // 123456
  119 +pad(1, 5); // "00001"
  120 +pad(12, 5); // "00012"
  121 +pad(123, 5); // "00123"
  122 +pad(1234, 5); // "01234"
  123 +pad(12345, 5); // "12345"
  124 +pad(123456, 5); // "123456"
  125 +
  126 +// you can also specify the "char" used for padding
  127 +pad(12, 5, '_'); // "___12"
125 128 ```
126 129
  130 +see: [string/lpad](./string.html#lpad)
  131 +
127 132
128 133
129 134 ## rol(val, shift):Number
8 src/date.js
@@ -3,13 +3,19 @@ define(function(require){
3 3 //automatically generated, do not edit!
4 4 //run `node build` instead
5 5 return {
  6 + 'dayOfTheYear' : require('./date/dayOfTheYear'),
6 7 'diff' : require('./date/diff'),
  8 + 'i18n_' : require('./date/i18n_'),
7 9 'isLeapYear' : require('./date/isLeapYear'),
8 10 'isSame' : require('./date/isSame'),
9 11 'parseIso' : require('./date/parseIso'),
10 12 'startOf' : require('./date/startOf'),
  13 + 'strftime' : require('./date/strftime'),
  14 + 'timezoneAbbr' : require('./date/timezoneAbbr'),
  15 + 'timezoneOffset' : require('./date/timezoneOffset'),
11 16 'totalDaysInMonth' : require('./date/totalDaysInMonth'),
12   - 'totalDaysInYear' : require('./date/totalDaysInYear')
  17 + 'totalDaysInYear' : require('./date/totalDaysInYear'),
  18 + 'weekOfTheYear' : require('./date/weekOfTheYear')
13 19 };
14 20
15 21 });
13 src/date/dayOfTheYear.js
... ... @@ -0,0 +1,13 @@
  1 +define(['../lang/isDate'], function (isDate) {
  2 +
  3 + /**
  4 + * return the day of the year (1..366)
  5 + */
  6 + function dayOfTheYear(date){
  7 + return (Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) -
  8 + Date.UTC(date.getFullYear(), 0, 1)) / 86400000 + 1;
  9 + }
  10 +
  11 + return dayOfTheYear;
  12 +
  13 +});
61 src/date/i18n/en-US.js
... ... @@ -0,0 +1,61 @@
  1 +define(function(){
  2 + // en-US (English, United States)
  3 + return {
  4 + "am" : "AM",
  5 + "pm" : "PM",
  6 +
  7 + "x": "%m/%d/%y",
  8 + "X": "%H:%M:%S",
  9 + "c": "%a %d %b %Y %I:%M:%S %p %Z",
  10 +
  11 + "months" : [
  12 + "January",
  13 + "February",
  14 + "March",
  15 + "April",
  16 + "May",
  17 + "June",
  18 + "July",
  19 + "August",
  20 + "September",
  21 + "October",
  22 + "November",
  23 + "December"
  24 + ],
  25 +
  26 + "months_abbr" : [
  27 + "Jan",
  28 + "Feb",
  29 + "Mar",
  30 + "Apr",
  31 + "May",
  32 + "Jun",
  33 + "Jul",
  34 + "Aug",
  35 + "Sep",
  36 + "Oct",
  37 + "Nov",
  38 + "Dec"
  39 + ],
  40 +
  41 + "days" : [
  42 + "Sunday",
  43 + "Monday",
  44 + "Tuesday",
  45 + "Wednesday",
  46 + "Thursday",
  47 + "Friday",
  48 + "Saturday"
  49 + ],
  50 +
  51 + "days_abbr" : [
  52 + "Sun",
  53 + "Mon",
  54 + "Tue",
  55 + "Wed",
  56 + "Thu",
  57 + "Fri",
  58 + "Sat"
  59 + ]
  60 + };
  61 +});
61 src/date/i18n/pt-BR.js
... ... @@ -0,0 +1,61 @@
  1 +define(function(){
  2 + // pt-BR (Brazillian Portuguese)
  3 + return {
  4 + "am" : "",
  5 + "pm" : "",
  6 +
  7 + "x": "%d/%m/%y",
  8 + "X": "%H:%M:%S",
  9 + "c": "%a %d %b %Y %H:%M:%S %Z",
  10 +
  11 + "months" : [
  12 + "Janeiro",
  13 + "Fevereiro",
  14 + "Março",
  15 + "Abril",
  16 + "Maio",
  17 + "Junho",
  18 + "Julho",
  19 + "Agosto",
  20 + "Setembro",
  21 + "Outubro",
  22 + "Novembro",
  23 + "Dezembro"
  24 + ],
  25 +
  26 + "months_abbr" : [
  27 + "Jan",
  28 + "Fev",
  29 + "Mar",
  30 + "Abr",
  31 + "Mai",
  32 + "Jun",
  33 + "Jul",
  34 + "Ago",
  35 + "Set",
  36 + "Out",
  37 + "Nov",
  38 + "Dez"
  39 + ],
  40 +
  41 + "days" : [
  42 + "Domingo",
  43 + "Segunda",
  44 + "Terça",
  45 + "Quarta",
  46 + "Quinta",
  47 + "Sexta",
  48 + "Sábado"
  49 + ],
  50 +
  51 + "days_abbr" : [
  52 + "Dom",
  53 + "Seg",
  54 + "Ter",
  55 + "Qua",
  56 + "Qui",
  57 + "Sex",
  58 + "Sáb"
  59 + ]
  60 + };
  61 +});
13 src/date/i18n_.js
... ... @@ -0,0 +1,13 @@
  1 +define(['../object/mixIn', './i18n/en-US'], function(mixIn, enUS){
  2 +
  3 + // we also use mixIn to make sure we don't affect the original locale
  4 + var activeLocale = mixIn({}, enUS, {
  5 + // we expose a "set" method to allow overriding the global locale
  6 + set : function(localeData){
  7 + mixIn(activeLocale, localeData);
  8 + }
  9 + });
  10 +
  11 + return activeLocale;
  12 +
  13 +});
113 src/date/strftime.js
... ... @@ -0,0 +1,113 @@
  1 +define(['../number/pad', './i18n_', './dayOfTheYear', './timezoneOffset', './timezoneAbbr', './weekOfTheYear'], function (pad, i18n, dayOfTheYear, timezoneOffset, timezoneAbbr, weekOfTheYear) {
  2 +
  3 + var _combinations = {
  4 + 'D': '%m/%d/%y',
  5 + 'F': '%Y-%m-%d',
  6 + 'r': '%I:%M:%S %p',
  7 + 'R': '%H:%M',
  8 + 'T': '%H:%M:%S',
  9 + 'x': 'locale',
  10 + 'X': 'locale',
  11 + 'c': 'locale'
  12 + };
  13 +
  14 +
  15 + /**
  16 + * format date based on strftime format
  17 + */
  18 + function strftime(date, format, localeData){
  19 + localeData = localeData || i18n;
  20 + var reToken = /%([a-z%])/gi;
  21 +
  22 + function makeIterator(fn) {
  23 + return function(match, token){
  24 + return fn(date, token, localeData);
  25 + };
  26 + }
  27 +
  28 + return format
  29 + .replace(reToken, makeIterator(expandCombinations))
  30 + .replace(reToken, makeIterator(convertToken));
  31 + }
  32 +
  33 +
  34 + function expandCombinations(date, token, l10n){
  35 + if (token in _combinations) {
  36 + var expanded = _combinations[token];
  37 + return expanded === 'locale'? l10n[token] : expanded;
  38 + } else {
  39 + return '%'+ token;
  40 + }
  41 + }
  42 +
  43 +
  44 + function convertToken(date, token, l10n){
  45 + switch (token){
  46 + case 'a':
  47 + return l10n.days_abbr[date.getDay()];
  48 + case 'A':
  49 + return l10n.days[date.getDay()];
  50 + case 'h':
  51 + case 'b':
  52 + return l10n.months_abbr[date.getMonth()];
  53 + case 'B':
  54 + return l10n.months[date.getMonth()];
  55 + case 'C':
  56 + return pad(Math.floor(date.getFullYear() / 100), 2);
  57 + case 'd':
  58 + return pad(date.getDate(), 2);
  59 + case 'e':
  60 + return pad(date.getDate(), 2, ' ');
  61 + case 'H':
  62 + return pad(date.getHours(), 2);
  63 + case 'I':
  64 + return pad(date.getHours() % 12, 2);
  65 + case 'j':
  66 + return pad(dayOfTheYear(date), 3);
  67 + case 'L':
  68 + return pad(date.getMilliseconds(), 3);
  69 + case 'm':
  70 + return pad(date.getMonth() + 1, 2);
  71 + case 'M':
  72 + return pad(date.getMinutes(), 2);
  73 + case 'n':
  74 + return '\n';
  75 + case 'p':
  76 + return date.getHours() >= 12? l10n.pm : l10n.am;
  77 + case 'P':
  78 + return convertToken(date, 'p', l10n).toLowerCase();
  79 + case 's':
  80 + return date.getTime() / 1000;
  81 + case 'S':
  82 + return pad(date.getSeconds(), 2);
  83 + case 't':
  84 + return '\t';
  85 + case 'u':
  86 + var day = date.getDay();
  87 + return day === 0? 7 : day;
  88 + case 'U':
  89 + return pad(weekOfTheYear(date), 2);
  90 + case 'w':
  91 + return date.getDay();
  92 + case 'W':
  93 + return pad(weekOfTheYear(date, 1), 2);
  94 + case 'y':
  95 + return pad(date.getFullYear() % 100, 2);
  96 + case 'Y':
  97 + return pad(date.getFullYear(), 4);
  98 + case 'z':
  99 + return timezoneOffset(date);
  100 + case 'Z':
  101 + return timezoneAbbr(date);
  102 + case '%':
  103 + return '%';
  104 + default:
  105 + // keep unrecognized tokens
  106 + return '%'+ token;
  107 + }
  108 + }
  109 +
  110 +
  111 + return strftime;
  112 +
  113 +});
17 src/date/timezoneAbbr.js
... ... @@ -0,0 +1,17 @@
  1 +define(['./timezoneOffset'], function(timezoneOffset) {
  2 +
  3 + /**
  4 + * Abbreviated time zone name or similar information.
  5 + */
  6 + function timezoneAbbr(date){
  7 + // Date.toString gives different results depending on the
  8 + // browser/system so we fallback to timezone offset
  9 + // chrome: 'Mon Apr 08 2013 09:02:04 GMT-0300 (BRT)'
  10 + // IE: 'Mon Apr 8 09:02:04 UTC-0300 2013'
  11 + var tz = /\(([A-Z]{3,4})\)/.exec(date.toString());
  12 + return tz? tz[1] : timezoneOffset(date);
  13 + }
  14 +
  15 + return timezoneAbbr;
  16 +
  17 +});
16 src/date/timezoneOffset.js
... ... @@ -0,0 +1,16 @@
  1 +define(['../number/pad'], function (pad) {
  2 +
  3 + /**
  4 + * time zone as hour and minute offset from UTC (e.g. +0900)
  5 + */
  6 + function timezoneOffset(date){
  7 + var offset = date.getTimezoneOffset();
  8 + var abs = Math.abs(offset);
  9 + var h = pad(Math.floor(abs / 60), 2);
  10 + var m = pad(abs % 60, 2);
  11 + return (offset > 0? '-' : '+') + h + m;
  12 + }
  13 +
  14 + return timezoneOffset;
  15 +
  16 +});
16 src/date/weekOfTheYear.js
... ... @@ -0,0 +1,16 @@
  1 +define(['./dayOfTheYear'], function (dayOfTheYear) {
  2 +
  3 + /**
  4 + * Return the week of the year based on given firstDayOfWeek
  5 + */
  6 + function weekOfTheYear(date, firstDayOfWeek){
  7 + firstDayOfWeek = firstDayOfWeek == null? 0 : firstDayOfWeek;
  8 + var doy = dayOfTheYear(date);
  9 + var dow = (7 + date.getDay() - firstDayOfWeek) % 7;
  10 + var relativeWeekDay = 6 - firstDayOfWeek - dow;
  11 + return Math.floor((doy + relativeWeekDay) / 7);
  12 + }
  13 +
  14 + return weekOfTheYear;
  15 +
  16 +});
4 src/number/pad.js
@@ -3,8 +3,8 @@ define(['../string/lpad'], function(lpad){
3 3 /**
4 4 * Add padding zeros if n.length < minLength.
5 5 */
6   - function pad(n, minLength){
7   - return lpad(''+ n, minLength, '0');
  6 + function pad(n, minLength, char){
  7 + return lpad(''+ n, minLength, char || '0');
8 8 }
9 9
10 10 return pad;
14 tests/spec/date/spec-dayOfTheYear.js
... ... @@ -0,0 +1,14 @@
  1 +define(['mout/date/dayOfTheYear'], function(dayOfTheYear){
  2 +
  3 + describe('date/dayOfTheYear', function(){
  4 +
  5 + it('should return day of the year', function(){
  6 + expect( dayOfTheYear(new Date(2013, 0, 1)) ).toBe( 1 );
  7 + expect( dayOfTheYear(new Date(2013, 0, 12)) ).toBe( 12 );
  8 + expect( dayOfTheYear(new Date(2013, 2, 12)) ).toBe( 71 );
  9 + expect( dayOfTheYear(new Date(2013, 11, 31)) ).toBe( 365 );
  10 + });
  11 +
  12 + });
  13 +
  14 +});
30 tests/spec/date/spec-i18n_.js
... ... @@ -0,0 +1,30 @@
  1 +define(['mout/date/i18n_'], function(i18n){
  2 +
  3 + describe('date/i18n_', function(){
  4 +
  5 + it('should contain localization info', function(){
  6 + expect( i18n.am ).toBeDefined();
  7 + expect( i18n.pm ).toBeDefined();
  8 + expect( i18n.months.length ).toBe(12);
  9 + expect( i18n.months_abbr.length ).toBe(12);
  10 + expect( i18n.days.length ).toBe(7);
  11 + expect( i18n.days_abbr.length ).toBe(7);
  12 + });
  13 +
  14 + it('should allow overriding the global data', function () {
  15 + var am = i18n.am;
  16 + var old = i18n;
  17 + i18n.set({
  18 + am: 'FOO'
  19 + });
  20 + expect( i18n.am ).toEqual('FOO');
  21 + expect( i18n ).toBe( old );
  22 + i18n.set({
  23 + am: am
  24 + });
  25 + expect( i18n.am ).toEqual('AM');
  26 + });
  27 +
  28 + });
  29 +
  30 +});
257 tests/spec/date/spec-strftime.js
... ... @@ -0,0 +1,257 @@
  1 +define(['mout/date/strftime'], function(strftime){
  2 +
  3 + describe('date/strftime', function(){
  4 +
  5 + var date = new Date(2013, 3, 8, 9, 2, 4);
  6 + var date_2 = new Date(2002, 11, 18, 19, 22, 43);
  7 +
  8 + // need to make sure we always have same timezoneOffset
  9 + date.getTimezoneOffset = date_2.getTimezoneOffset = function(){
  10 + return 0;
  11 + };
  12 +
  13 +
  14 + it('should return abbreviated week day name on %a', function(){
  15 + expect( strftime(date, '%a') ).toBe('Mon');
  16 + expect( strftime(date_2, '%a') ).toBe('Wed');
  17 + });
  18 +
  19 + it('should return full week day name for %A', function () {
  20 + expect( strftime(date, '%A') ).toBe('Monday');
  21 + expect( strftime(date_2, '%A') ).toBe('Wednesday');
  22 + });
  23 +
  24 + it('should return abbreviated month for %b and %h', function () {
  25 + expect( strftime(date, '%b') ).toBe('Apr');
  26 + expect( strftime(date, '%h') ).toBe('Apr');
  27 + expect( strftime(date_2, '%b') ).toBe('Dec');
  28 + expect( strftime(date_2, '%h') ).toBe('Dec');
  29 + });
  30 +
  31 + it('should return full month name for %B', function () {
  32 + expect( strftime(date, '%B') ).toBe('April');
  33 + expect( strftime(date_2, '%B') ).toBe('December');
  34 + });
  35 +
  36 + it('should return locale datetime representation for %c', function () {
  37 + var d1 = new Date(+date);
  38 + d1.toString = function(){
  39 + return 'Mon Apr 08 2013 09:02:04 GMT-0300 (BRT)';
  40 + };
  41 + expect( strftime(d1, '%c') ).toEqual('Mon 08 Apr 2013 09:02:04 AM BRT');
  42 +
  43 + var d2 = new Date(+date_2);
  44 + d2.toString = function(){
  45 + return 'Wed Dec 18 2002 19:22:43 GMT-0200 (BRST)';
  46 + };
  47 + expect( strftime(d2, '%c') ).toEqual('Wed 18 Dec 2002 07:22:43 PM BRST');
  48 + });
  49 +
  50 + it('should return the century number for %C', function () {
  51 + expect( strftime(date, '%C') ).toBe('20');
  52 + });
  53 +
  54 + it('should return zero-padded day of the month on %d', function () {
  55 + expect( strftime(date, '%d') ).toBe('08');
  56 + expect( strftime(date_2, '%d') ).toBe('18');
  57 + });
  58 +
  59 + it('should return Date on %D and on %x', function () {
  60 + expect( strftime(date, '%D') ).toEqual('04/08/13');
  61 + expect( strftime(date_2, '%D') ).toEqual('12/18/02');
  62 + expect( strftime(date, '%x') ).toEqual('04/08/13');
  63 + expect( strftime(date_2, '%x') ).toEqual('12/18/02');
  64 + });
  65 +
  66 + it('should return blank-padded day of the month on %e', function () {
  67 + expect( strftime(date, '%e') ).toBe(' 8');
  68 + expect( strftime(date_2, '%e') ).toBe('18');
  69 + });
  70 +
  71 + it('should return ISO 8601 date format for %F', function () {
  72 + expect( strftime(date, '%F') ).toBe('2013-04-08');
  73 + expect( strftime(date_2, '%F') ).toBe('2002-12-18');
  74 + });
  75 +
  76 + it('should return zero-padded hour (24h) for %H', function () {
  77 + expect( strftime(date, '%H') ).toBe('09');
  78 + expect( strftime(date_2, '%H') ).toBe('19');
  79 + });
  80 +
  81 + it('should return zero-padded hour (12h) for %H', function () {
  82 + expect( strftime(date, '%I') ).toBe('09');
  83 + expect( strftime(date_2, '%I') ).toBe('07');
  84 + });
  85 +
  86 + it('should return day of the year for %j', function () {
  87 + expect( strftime(date, '%j') ).toEqual( '098' );
  88 + expect( strftime(date_2, '%j') ).toEqual( '352' );
  89 +
  90 + });
  91 +
  92 + it('should return zero-padded milliseconds for %L', function () {
  93 + expect( strftime(date, '%L') ).toBe('000');
  94 + expect( strftime({
  95 + getMilliseconds:function(){
  96 + return 55;
  97 + }
  98 + }, '%L') ).toBe('055');
  99 + });
  100 +
  101 + it('should return zero-padded month for %m', function () {
  102 + expect( strftime(date, '%m') ).toBe('04');
  103 + expect( strftime(date_2, '%m') ).toBe('12');
  104 + });
  105 +
  106 + it('should return zero-padded minutes for %M', function () {
  107 + expect( strftime(date, '%M') ).toBe('02');
  108 + expect( strftime(date_2, '%M') ).toBe('22');
  109 + });
  110 +
  111 + it('should return newline char for %n', function () {
  112 + expect( strftime(date, '%n') ).toBe('\n');
  113 + });
  114 +
  115 + it('should return AM/PM for %p', function () {
  116 + expect( strftime(date, '%p') ).toBe('AM');
  117 + expect( strftime(date_2, '%p') ).toBe('PM');
  118 + });
  119 +
  120 + it('should return lowercase am/pm for %p', function () {
  121 + expect( strftime(date, '%P') ).toBe('am');
  122 + expect( strftime(date_2, '%P') ).toBe('pm');
  123 + });
  124 +
  125 + it('should return 12-hour time (%I:%M:%S %p) for %r', function () {
  126 + expect( strftime(date, '%r') ).toBe('09:02:04 AM');
  127 + expect( strftime(date_2, '%r') ).toBe('07:22:43 PM');
  128 + });
  129 +
  130 + it('should return 24-hour time (%H:%M) for %R', function () {
  131 + expect( strftime(date, '%R') ).toBe('09:02');
  132 + expect( strftime(date_2, '%R') ).toBe('19:22');
  133 + });
  134 +
  135 + it('should return number of seconds since epoch for %s', function () {
  136 + expect( strftime(date, '%s') ).toBe( String(date / 1000) );
  137 + });
  138 +
  139 + it('should return zero-padded seconds for %S', function () {
  140 + expect( strftime(date, '%S') ).toBe('04');
  141 + expect( strftime(date_2, '%S') ).toBe('43');
  142 + });
  143 +
  144 + it('should return tab char for %t', function () {
  145 + expect( strftime(date, '%t') ).toBe('\t');
  146 + });
  147 +
  148 + it('should return 24-hour time (%H:%M:%S) for %T and %X', function () {
  149 + expect( strftime(date, '%T') ).toBe('09:02:04');
  150 + expect( strftime(date, '%X') ).toBe('09:02:04');
  151 + });
  152 +
  153 + it('should return Weekday as a decimal number (Sunday is 7) for %u', function () {
  154 + expect( strftime(date, '%u') ).toBe('1');
  155 + expect( strftime(date_2, '%u') ).toBe('3');
  156 + expect( strftime(new Date(2013, 3, 7), '%u') ).toBe('7');
  157 + });
  158 +
  159 + it('should return week number of the year, starting at Sunday for %U', function () {
  160 + expect( strftime(new Date(2013, 0, 5), '%U') ).toEqual( '00' );
  161 + expect( strftime(new Date(2013, 0, 6), '%U') ).toEqual( '01' );
  162 + expect( strftime(date, '%U') ).toEqual( '14' );
  163 + expect( strftime(date_2, '%U') ).toEqual( '50' );
  164 + });
  165 +
  166 + it('should return Weekday as a decimal number (Sunday is 0) for %w', function () {
  167 + expect( strftime(date, '%w') ).toBe('1');
  168 + expect( strftime(date_2, '%w') ).toBe('3');
  169 + expect( strftime(new Date(2013, 3, 7), '%w') ).toBe('0');
  170 + });
  171 +
  172 + it('should return week number of the year, starting at Monday for %W', function () {
  173 + expect( strftime(new Date(2013, 0, 6), '%W') ).toEqual( '00' );
  174 + expect( strftime(new Date(2013, 0, 7), '%W') ).toEqual( '01' );
  175 + expect( strftime(date, '%W') ).toEqual( '14' );
  176 + expect( strftime(date_2, '%W') ).toEqual( '50' );
  177 + });
  178 +
  179 + it('should return year without century for %y', function () {
  180 + expect( strftime(date, '%y') ).toBe('13');
  181 + expect( strftime(date_2, '%y') ).toBe('02');
  182 + });
  183 +
  184 + it('should return year with century for %Y', function () {
  185 + expect( strftime(date, '%Y') ).toBe('2013');
  186 + expect( strftime(date_2, '%Y') ).toBe('2002');
  187 + });
  188 +
  189 + it('should return timezone offset for %z', function () {
  190 + expect( strftime(date, '%z') ).toEqual('+0000');
  191 + expect( strftime({
  192 + getTimezoneOffset : function(){
  193 + return 210;
  194 + }
  195 + }, '%z') ).toBe('-0330');
  196 + expect( strftime({
  197 + getTimezoneOffset : function(){
  198 + return -210;
  199 + }
  200 + }, '%z') ).toBe('+0330');
  201 + });
  202 +
  203 + it('should return timezone abbreviation for %Z', function () {
  204 + // note that result will vary based on the browser
  205 + // and user system so we "fake it" during the tests and fallback
  206 + // to timezoneOffset in case we can't extract the TZ from the
  207 + // Date.toString()
  208 + // chrome 28 / FF 18: 'Mon Apr 08 2013 09:02:04 GMT-0300 (BRT)'
  209 + // IE7: 'Mon Apr 8 09:02:04 UTC-0300 2013'
  210 +
  211 + expect( strftime({
  212 + toString : function(){
  213 + return 'Mon Apr 08 2013 09:02:04 GMT-0300 (BRT)';
  214 + }
  215 + }, '%Z') ).toEqual('BRT');
  216 +
  217 + expect( strftime({
  218 + getTimezoneOffset : function(){
  219 + return 180;
  220 + },
  221 + toString : function(){
  222 + return 'Mon Apr 8 09:02:04 UTC-0300 2013';
  223 + }
  224 + }, '%Z') ).toEqual('-0300');
  225 + });
  226 +
  227 +
  228 + it('should support escaping %%', function () {
  229 + expect( strftime(date, '%%') ).toEqual('%');
  230 + });
  231 +
  232 + it('should support multiple tokens at once', function () {
  233 + // iso8601
  234 + expect( strftime(date, '%Y-%m-%dT%H:%M:%S%z') ).toEqual( '2013-04-08T09:02:04+0000' );
  235 + });
  236 +
  237 + it('should support custom locale', function () {
  238 +
  239 + var ptBr = {
  240 + "months" : ["Janeiro", "Fevereiro", "Março", "Abril",
  241 + "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro",
  242 + "Novembro", "Dezembro"],
  243 + "months_abbr" : ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun",
  244 + "Jul", "Ago", "Set", "Out", "Nov", "Dec"],
  245 + "days" : ["Domingo", "Segunda", "Terça", "Quarta", "Quinta",
  246 + "Sexta", "Sábado"],
  247 + "days_abbr" : ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]
  248 + };
  249 +
  250 + expect( strftime(date, '%a, %d %b', ptBr) ).toEqual( 'Seg, 08 Abr' );
  251 + expect( strftime(date, '%A, %d %B', ptBr) ).toEqual( 'Segunda, 08 Abril' );
  252 + });
  253 +
  254 +
  255 + });
  256 +
  257 +});
28 tests/spec/date/spec-timezoneAbbr.js
... ... @@ -0,0 +1,28 @@
  1 +define(['mout/date/timezoneAbbr'], function(timezoneAbbr){
  2 +
  3 + describe('date/timezoneAbbr', function(){
  4 +
  5 + it('should return timezone abbreviation or similar data', function(){
  6 + // we use fake date objects because timezone info will be different
  7 + // based on user system (tests should be deterministic)
  8 + expect( timezoneAbbr({
  9 + toString : function(){
  10 + // FF/Chrome/Safari
  11 + return 'Mon Apr 08 2013 09:02:04 GMT-0300 (BRT)';
  12 + }
  13 + }) ).toEqual('BRT');
  14 +
  15 + expect( timezoneAbbr({
  16 + getTimezoneOffset : function(){
  17 + return 180;
  18 + },
  19 + toString : function(){
  20 + // IE doesn't return TZ abbr info so we fallback to offset
  21 + return 'Mon Apr 8 09:02:04 UTC-0300 2013';
  22 + }
  23 + }) ).toEqual('-0300');
  24 + });
  25 +
  26 + });
  27 +
  28 +});
24 tests/spec/date/spec-timezoneOffset.js
... ... @@ -0,0 +1,24 @@
  1 +define(['mout/date/timezoneOffset'], function(timezoneOffset){
  2 +
  3 + describe('date/timezoneOffset', function(){
  4 +
  5 + it('should return time zone as hour and minute offset from UTC (e.g. +0900)', function(){
  6 + // we mock the date object since getTimezoneOffset always returns
  7 + // value based on system locale
  8 + expect( timezoneOffset({
  9 + getTimezoneOffset: function(){
  10 + return 180;
  11 + }
  12 + }) ).toBe('-0300');
  13 +
  14 + expect( timezoneOffset({
  15 + getTimezoneOffset: function(){
  16 + return -210;
  17 + }
  18 + }) ).toBe('+0330');
  19 +
  20 + });
  21 +
  22 + });
  23 +
  24 +});
32 tests/spec/date/spec-weekOfTheYear.js
... ... @@ -0,0 +1,32 @@
  1 +define(['mout/date/weekOfTheYear'], function(weekOfTheYear){
  2 +
  3 + describe('date/weekOfTheYear', function(){
  4 +
  5 + it('should return the week of year starting from Sunday', function(){
  6 + expect( weekOfTheYear(new Date(2013,0,1)) ).toBe( 0 );
  7 + expect( weekOfTheYear(new Date(2013,0,6)) ).toBe( 1 );
  8 + expect( weekOfTheYear(new Date(2013,0,9)) ).toBe( 1 );
  9 + expect( weekOfTheYear(new Date(2013,11,9)) ).toBe( 49 );
  10 + expect( weekOfTheYear(new Date(2013,11,31)) ).toBe( 52 );
  11 + });
  12 +
  13 + it('should allow custom firstDayOfWeek', function(){
  14 + expect( weekOfTheYear(new Date(2013,0,1), 1) ).toBe( 0 );
  15 + expect( weekOfTheYear(new Date(2013,0,6), 1) ).toBe( 0 );
  16 + expect( weekOfTheYear(new Date(2013,0,7), 1) ).toBe( 1 );
  17 + expect( weekOfTheYear(new Date(2013,0,9), 1) ).toBe( 1 );
  18 + expect( weekOfTheYear(new Date(2013,11,9), 1) ).toBe( 49 );
  19 + expect( weekOfTheYear(new Date(2013,11,31), 1) ).toBe( 52 );
  20 + });
  21 +
  22 + it('should handle years that start at Sunday', function(){
  23 + expect( weekOfTheYear(new Date(2012,0,1)) ).toBe( 1 );
  24 + expect( weekOfTheYear(new Date(2012,0,6)) ).toBe( 1 );
  25 + expect( weekOfTheYear(new Date(2012,0,9)) ).toBe( 2 );
  26 + expect( weekOfTheYear(new Date(2012,0,1), 1) ).toBe( 0 );
  27 + expect( weekOfTheYear(new Date(2012,0,6), 1) ).toBe( 1 );
  28 + });
  29 +
  30 + });
  31 +
  32 +});
4 tests/spec/number/spec-pad.js
@@ -8,6 +8,10 @@ define(['mout/number/pad'], function (pad) {
8 8 expect( pad(15, 3) ).toEqual( '015' );
9 9 expect( pad(15, 4) ).toEqual( '0015' );
10 10 });
  11 +
  12 + it('should allow custom pad char', function () {
  13 + expect( pad(15, 4, '_') ).toEqual( '__15' );
  14 + });
11 15 });
12 16
13 17 });
8 tests/spec/spec-date.js
... ... @@ -1,13 +1,19 @@
1 1 //automatically generated, do not edit!
2 2 //run `node build` instead
3 3 define([
  4 + './date/spec-dayOfTheYear',
4 5 './date/spec-diff',
  6 + './date/spec-i18n_',
5 7 './date/spec-isLeapYear',
6 8 './date/spec-isSame',
7 9 './date/spec-parseIso',
8 10 './date/spec-startOf',
  11 + './date/spec-strftime',
  12 + './date/spec-timezoneAbbr',
  13 + './date/spec-timezoneOffset',
9 14 './date/spec-totalDaysInMonth',
10   - './date/spec-totalDaysInYear'
  15 + './date/spec-totalDaysInYear',
  16 + './date/spec-weekOfTheYear'
11 17 ], function(){
12 18 //noop
13 19 });

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.