Merge pull request #1 from iFixit/update-strftime-for-localization

Update strftime for localization
This commit is contained in:
Andrew Pirondini 2015-07-20 11:41:11 -07:00
commit 870e4e3771
10 changed files with 1230 additions and 631 deletions

244
Changelog.md Normal file
View file

@ -0,0 +1,244 @@
v0.9.2 on 2015-05-29
--------------------
- fix a caching bug, which was a regression in 0.9.0 ([issue #63](https://github.com/samsonjs/strftime/issues/63))
- update license attribute in package.json as required by npm
Thanks to [Peter deHaan](https://github.com/pdehaan)
- construct GMT times used in tests in a more robust way
- fix a bug calculating week numbers ([issue #56](https://github.com/samsonjs/strftime/issues/56))
Thanks to [Alexandr Nikitin](https://github.com/alexandrnikitin)
- warn about possible misuse of %:: or %::: modifiers
v0.9.1 on 2015-03-16
--------------------
- re-fix [issue #38](https://github.com/samsonjs/strftime/pull/38) which was lost in the v0.9 merge
- add this changelog
v0.9.0 on 2015-03-15
--------------------
This release marks the final run up to v1.0, which should be released by March 2016.
The headline feature is a huge performance boost resulting from [this contest](http://hola.org/challenge_js). [Alexandr Nikitin](https://github.com/alexandrnikitin) has essentially [rewritten](https://github.com/samsonjs/strftime/pull/41) the code and [the results](http://jsperf.com/strftime-optimization/2) speak for themselves.
Along with this the API has been unified and cleaned up. `strftimeTZ`, `strftimeUTC`, and `localizedStrftime` have all been deprecated in favour of the following functions: `timezone(tz)`, `utc()`, and `localize(locale)`. You use them like so:
```JavaScript
var strftime = require('strftime'); // not required in web browsers
var strftimeIT = strftime.localize(anItalianLocale);
var strftimePST = strftime.timezone('-0800');
var strftimeUTC = strftime.utc();
// You can combine them
var strftimeIT_PST = strftimeIT.timezone('-0800');
// And chain them all at once
var strftimeIT_PST = strftime.localize(anItalianLocale).timezone('-0800');
```
The previous API is deprecated and will be removed for v1.0. The good news is that the previous API is supported by adapting the new API, so you get most of the performance benefits before you even update your code to use the new API.
The new API does not support passing in a custom timezone or locale on each call to `strftime`. This is a regression so if you really need this use case [let us know](https://github.com/samsonjs/strftime/issues/new) and we'll figure something out.
Thanks to all contributors that have helped to improve this library over the past 4 years.
v0.8.4 on 2015-03-05
--------------------
- fix conversion of dates to UTC
Thanks to [Alexandr Nikitin](https://github.com/alexandrnikitin)
- extend `%z` with a colon separator in timezone offsets, `"[+-]HH:MM"`
Thanks to [Cory Heslip](https://github.com/cheslip)
- ignore irrelevant files in bower.json
v0.8.3 on 2015-02-08
--------------------
First release for [Bower](http://bower.io), and only released for Bower.
v0.8.2 on 2014-08-08
--------------------
- fix `%e` which is supposed to be padded with a space
v0.8.1 on 2014-06-17
--------------------
- fix `%Z` when the timezone contains spaces
Thanks to [w0den](https://github.com/w0den)
- fix examples using `%Y` in the readme
Thanks to [Ryan Regalado](https://github.com/d48)
- fix a bug when specifying minutes in the timezone
Thanks to [Alexandr Nikitin](https://github.com/alexandrnikitin)
v0.8.0 on 2014-01-29
--------------------
- allow timezones to be specified as strings of the form `"+0100"` or `"-0800"` (`[+-]HHMM`)
- fix a bug running tests where the environment variable `TZ` is empty and the system timezone is not PST/PDT
v0.7.0 on 2013-11-08
--------------------
- add support for passing in explicit timezones with `strftimeTZ` which accepts numeric offsets from GMT, in minutes
v0.6.2 on 2013-08-29
--------------------
- expose `strftimeUTC` and `localizedStrftime` properly in browsers
v0.6.1 on 2013-06-13
--------------------
- fix a bug where `RequiredDateMethods` was created as a global
v0.6.0 on 2013-05-15
--------------------
- add `%o` to get the day of the month as an ordinal (in English)
v0.5.2 on 2013-04-07
--------------------
- add some Ruby extension prefixes: `-`, `_`, and `0`, and they work like so:
`strftime('%-d') // => "7"`
`strftime('%_d') // => " 7"`
`strftime('%0d') // => "07"`
- fix padding the day-of-year in `%j`
- add a minified version of the code to the repo for easy distribution with some package managers
v0.5.1 on 2013-03-07
--------------------
- remove deprecated `getLocalizedStrftime` function
- make `%C` pad the century with spaces, like C
- list all supported specifiers in the readme, it's no longer fair to say "look at `man 3 strftime`"
- use fixed dates in the readme instead of "now", so people can execute the examples and see the same results
Thanks to [John Zwinck](https://github.com/jzwinck)
- fix `%z` for timezones greater than GMT
- support any `Date`-like objects instead of checking for actual instances of `Date`
v0.5.0 on 2013-01-05
--------------------
- add week numbers `%U` and `%W`
- add support for [component](https://github.com/componentjs/component)
Thanks to [TJ Holowaychuk](https://github.com/tj)
v0.4.8 on 2012-11-13
--------------------
- add `%j` and `%C`, thanks to [Ryan Stafford](https://github.com/ryanstafford)
v0.4.7 on 2012-06-08
--------------------
- add `%P` which is "am" or "pm", like `%p` but lowercase
(this makes no sense, and I am sorry for propagating this madness)
Thanks to [Rob Colburn](https://github.com/robcolburn)
- export the `strftime` function directly in [node](https://nodejs.org) so you can write `strftime = require('strftime')` instead of `strftime = require('strftime').strftime`
- added contributors to the readme and package.json
v0.4.6 on 2011-06-13
--------------------
- rename `getLocalizedStrftime(locale)` to `localizedStrftime(locale)`
The old name is deprecated and will stick around until v0.5 or v0.6.
- add tests for locales
v0.4.5 on 2011-06-08
--------------------
- fix the sign of `%z`, which is something like "+0100" or "-0800" (`[+-]HHMM`)
- improve test coverage
v0.4.4 on 2011-06-07
--------------------
- fix `%L` for values < 100
- convert tests from CoffeeScript to JavaScript
(nothing personal, just keeping the dependencies trim)
v0.4.3 on 2011-06-05
--------------------
This release was all [Andrew Schaaf](https://github.com/andrewschaaf).
- add some tests
- fix `%s` which is seconds since the Unix epoch, but was in milliseconds
- add `%L` for 3-digit milliseconds
v0.4.2 on 2011-06-05
--------------------
- add `strftimeUTC` for ignoring timezones
Thanks to [Andrew Schaaf](https://github.com/andrewschaaf)
- support exporting to the top level object in ES5 strict mode
v0.4.1 on 2011-06-02
--------------------
- fix `%y` for years outside the range [1900, 2099]
v0.4.0 on 2011-04-28
--------------------
- add support for localization
v0.3.0 on 2010-12-17
--------------------
- fix export for browsers
v0.2.3 on 2010-12-15
--------------------
- set required [node](https://nodejs.org) version to 0.2 instead of 0.3 in package.json
v0.2.2 on 2010-11-14
--------------------
- fix module export
v0.2.1 on 2010-11-11
--------------------
- bug fix for recursive formats
v0.2.0 on 2010-11-11
--------------------
- use `String.prototype.replace` instead of a `for` loop
v0.1.0 on 2010-11-11
--------------------
Initial release.

View file

@ -5,11 +5,11 @@ real-minify: strftime.js
closure <strftime.js >|strftime-min.js closure <strftime.js >|strftime-min.js
test: test:
TZ=America/Vancouver node test/test.js TZ=America/Vancouver node test.js
TZ=CET node test/test.js TZ=CET node test.js
test-minified: minify test-minified: minify
TZ=America/Vancouver node test/test.js ../strftime-min.js TZ=America/Vancouver node test.js ./strftime-min.js
TZ=CET node test/test.js ../strftime-min.js TZ=CET node test.js ./strftime-min.js
.PHONY: test test-minified .PHONY: test test-minified

135
Readme.md
View file

@ -1,64 +1,94 @@
strftime strftime
======== ========
strftime for JavaScript, works in Node.js and browsers, supports localization. strftime for JavaScript. Works in (at least) node.js and browsers. Supports localization and timezones. Most standard specifiers from C are supported as well as some other extensions from Ruby.
Most standard specifiers from C are supported as well as some other extensions
from Ruby.
Installation Installation
============ ============
npm install strftime [node](https://nodejs.org):
npm install strftime
[bower](http://bower.io):
bower install strftime
[component](https://github.com/componentjs/component):
component install samsonjs/strftime
Or you can copy [strftime.js](https://github.com/samsonjs/strftime/blob/master/strftime.js) wherever you want to use it, whether that's with a &lt;script&gt; tag or `require` or anything else.
The New API in 0.9
==================
The current version, 0.9, deprecates the older API that exported several functions: `strftimeTZ`, `strftimeUTC`, and `localizedStrftime`. In addition to this the exported function referenced itself as `require('strftime').strftime` or `window.strftime.strftime` for consistency with the other functions. *These functions are deprecated in 0.9 and will be removed in 1.0.*
Now you only need the single object exported and you can create a specialized version of it using the functions `utc()`, `localize(locale)`, and `timezone(offset)`. You can no longer pass in a timezone or locale on each call to `strftime` which is a regression. If you need this let me know and we will add it back into the API.
[More details are available in the changelog](https://github.com/samsonjs/strftime/blob/master/Changelog.md).
Usage Usage
===== =====
var strftime = require('strftime') ```JavaScript
var strftime = require('strftime') // not required in browsers
console.log(strftime('%B %d, %Y %H:%M:%S')) // => April 28, 2011 18:21:08 console.log(strftime('%B %d, %Y %H:%M:%S')) // => April 28, 2011 18:21:08
console.log(strftime('%F %T', new Date(1307472705067))) // => 2011-06-07 18:51:45 console.log(strftime('%F %T', new Date(1307472705067))) // => 2011-06-07 18:51:45
```
If you want to localize it: If you want to localize it:
var strftime = require('strftime') ```JavaScript
var strftime = require('strftime') // not required in browsers
var it_IT = { var it_IT = {
days: [ 'domenica', 'lunedi', 'martedi', 'mercoledi', 'giovedi', 'venerdi', 'sabato' ], days: ['domenica', 'lunedi', 'martedi', 'mercoledi', 'giovedi', 'venerdi', 'sabato'],
shortDays: [ 'dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab' ], shortDays: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'],
months: ['gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', 'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre'],
months: [ 'gennaio', 'febbraio', 'marzo', 'aprile', 'maggio', 'giugno', 'luglio', shortMonths: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'],
'agosto', 'settembre', 'ottobre', 'novembre', 'dicembre' ],
shortMonths: [ 'gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago',
'set', 'ott', 'nov', 'dic' ],
AM: 'AM', AM: 'AM',
PM: 'PM' PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
D: '%m/%d/%y',
F: '%Y-%m-%d',
R: '%H:%M',
X: '%T',
c: '%a %b %d %X %Y',
r: '%I:%M:%S %p',
T: '%H:%M:%S',
v: '%e-%b-%Y',
x: '%D'
} }
console.log(strftime('%B %d, %Y %H:%M:%S', it_IT)) // => aprile 28, 2011 18:21:08 }
console.log(strftime('%B %d, %Y %H:%M:%S', new Date(1307472705067), it_IT)) // => giugno 7, 2011 18:51:45 var strftimeIT = strftime.localize(it_IT)
console.log(strftimeIT('%B %d, %Y %H:%M:%S')) // => aprile 28, 2011 18:21:08
And if you don't want to pass a localization object every time you can get a localized `strftime` function like so: console.log(strftimeIT('%B %d, %Y %H:%M:%S', new Date(1307472705067))) // => giugno 7, 2011 18:51:45
```
var strftime = require('strftime')
var it_IT = { /* same as above */ }
var strftime_IT = strftime.localizedStrftime(it_IT)
console.log(strftime_IT('%B %d, %Y %H:%M:%S')) // aprile 28, 2011 18:21:08
Time zones can be passed in as an offset from GMT in minutes. Time zones can be passed in as an offset from GMT in minutes.
var strftimeTZ = require('strftime').strftimeTZ ```JavaScript
console.log(strftimeTZ('%B %d, %y %H:%M:%S', new Date(1307472705067), -420)) // => June 07, 11 11:51:45 var strftime = require('strftime') // not required in browsers
console.log(strftimeTZ('%F %T', new Date(1307472705067), 120)) // => 2011-06-07 20:51:45 var strftimePDT = strftime.timezone(-420)
var strftimeCEST = strftime.timezone(120)
console.log(strftimePDT('%B %d, %y %H:%M:%S', new Date(1307472705067))) // => June 07, 11 11:51:45
console.log(strftimeCEST('%F %T', new Date(1307472705067))) // => 2011-06-07 20:51:45
```
Alternatively you can use the timezone format used by ISO 8601, `+HHMM` or `-HHMM`. Alternatively you can use the timezone format used by ISO 8601, `+HHMM` or `-HHMM`.
var strftimeTZ = require('strftime').strftimeTZ ```JavaScript
console.log(strftimeTZ('', new Date(1307472705067), '-0700')) // => June 07, 11 11:51:45 var strftime = require('strftime') // not required in browsers
console.log(strftimeTZ('%F %T', new Date(1307472705067), '+0200')) // => 2011-06-07 20:51:45 var strftimePDT = strftime.timezone('-0700')
var strftimeCEST = strftime.timezone('+0200')
console.log(strftimePDT('%F %T', new Date(1307472705067))) // => June 07, 11 11:51:45
console.log(strftimeCEST('%F %T', new Date(1307472705067))) // => 2011-06-07 20:51:45
```
Supported Specifiers Supported Specifiers
==================== ====================
@ -73,10 +103,11 @@ e.g. `%q` becomes `q`. Use `%%` to get a literal `%` sign.
- B: full month name - B: full month name
- b: abbreviated month name - b: abbreviated month name
- C: AD century (year / 100), padded to 2 digits - C: AD century (year / 100), padded to 2 digits
- D: equivalent to `%m/%d/%y` - c: equivalent to `%a %b %d %X %Y` in en-US (based on locale)
- D: equivalent to `%m/%d/%y` in en-US (based on locale)
- d: day of the month, padded to 2 digits (01-31) - d: day of the month, padded to 2 digits (01-31)
- e: day of the month, padded with a leading space for single digit values (1-31) - e: day of the month, padded with a leading space for single digit values (1-31)
- F: equivalent to `%Y-%m-%d` - F: equivalent to `%Y-%m-%d` in en-US (based on locale)
- H: the hour (24-hour clock), padded to 2 digits (00-23) - H: the hour (24-hour clock), padded to 2 digits (00-23)
- h: the same as %b (abbreviated month name) - h: the same as %b (abbreviated month name)
- I: the hour (12-hour clock), padded to 2 digits (01-12) - I: the hour (12-hour clock), padded to 2 digits (01-12)
@ -88,19 +119,21 @@ e.g. `%q` becomes `q`. Use `%%` to get a literal `%` sign.
- m: the month, padded to 2 digits (01-12) - m: the month, padded to 2 digits (01-12)
- n: newline character - n: newline character
- o: day of the month as an ordinal (without padding), e.g. 1st, 2nd, 3rd, 4th, ... - o: day of the month as an ordinal (without padding), e.g. 1st, 2nd, 3rd, 4th, ...
- P: "am" or "pm" in lowercase [Ruby extension] - P: "am" or "pm" in lowercase (Ruby extension, based on locale)
- p: "AM" or "PM" - p: "AM" or "PM" (based on locale)
- R: equivalent to `%H:%M` - R: equivalent to `%H:%M` in en-US (based on locale)
- r: equivalent to `%I:%M:%S %p` - r: equivalent to `%I:%M:%S %p` in en-US (based on locale)
- S: the second, padded to 2 digits (00-60) - S: the second, padded to 2 digits (00-60)
- s: the number of seconds since the Epoch, UTC - s: the number of seconds since the Epoch, UTC
- T: equivalent to `%H:%M:%S` - T: equivalent to `%H:%M:%S` in en-US (based on locale)
- t: tab character - t: tab character
- U: week number of the year, Sunday as the first day of the week, padded to 2 digits (00-53) - U: week number of the year, Sunday as the first day of the week, padded to 2 digits (00-53)
- u: the weekday, Monday as the first day of the week (1-7) - u: the weekday, Monday as the first day of the week (1-7)
- v: equivalent to `%e-%b-%Y` - v: equivalent to `%e-%b-%Y` in en-US (based on locale)
- W: week number of the year, Monday as the first day of the week, padded to 2 digits (00-53) - W: week number of the year, Monday as the first day of the week, padded to 2 digits (00-53)
- w: the weekday, Sunday as the first day of the week (0-6) - w: the weekday, Sunday as the first day of the week (0-6)
- X: equivalent to `%D` in en-US (based on locale)
- x: equivalent to `%T` in en-US (based on locale)
- Y: the year with the century - Y: the year with the century
- y: the year without the century (00-99) - y: the year without the century (00-99)
- Z: the time zone name, replaced with an empty string if it is not found - Z: the time zone name, replaced with an empty string if it is not found
@ -108,28 +141,30 @@ e.g. `%q` becomes `q`. Use `%%` to get a literal `%` sign.
of UTC and a minus sign for those west of UTC, hours and minutes follow each of UTC and a minus sign for those west of UTC, hours and minutes follow each
padded to 2 digits and with no delimiter between them padded to 2 digits and with no delimiter between them
For more detail see `man 3 strftime` as the format specifiers should behave For more detail see `man 3 strftime` as the format specifiers should behave identically. If behaviour differs please [file a bug](https://github.com/samsonjs/strftime/issues/new).
identically. If behaviour differs please [file a bug](https://github.com/samsonjs/strftime/issues/new).
Any specifier can be modified with `-`, `_`, or `0` as well, as in Ruby. Any specifier can be modified with `-`, `_`, `0`, or `:` as well, as in Ruby. Using `%-` will omit any leading zeroes or spaces, `%_` will force spaces for padding instead of the default, and `%0` will force zeroes for padding. There's some redundancy here as `%-d` and `%e` have the same result, but it solves some awkwardness with formats like `%l`. Using `%:` for time zone offset, as in `%:z` will insert a colon as a delimiter.
Using `%-` will omit any leading zeroes or spaces, `%_` will force spaces
for padding instead of the default, and `%0` will force zeroes for padding.
There's some redundancy here as `%-d` and `%e` have the same result, but it
solves some awkwardness with formats like `%l`.
Contributors Contributors
============ ============
* [Rob Colburn](https://github.com/robcolburn)
* [Cory Heslip](https://github.com/cheslip)
* [TJ Holowaychuk](https://github.com/tj)
* [Forbes Lindesay](https://github.com/ForbesLindesay)
* [Alexandr Nikitin](https://github.com/alexandrnikitin)
* [Ryan Regalado](https://github.com/d48)
* [Sami Samhuri](https://github.com/samsonjs) * [Sami Samhuri](https://github.com/samsonjs)
* [Andrew Schaaf](https://github.com/andrewschaaf) * [Andrew Schaaf](https://github.com/andrewschaaf)
* [Rob Colburn](https://github.com/robcolburn)
* [Ryan Stafford](https://github.com/ryanstafford) * [Ryan Stafford](https://github.com/ryanstafford)
* [w0den](https://github.com/w0den)
* [John Zwinck](https://github.com/jzwinck)
License License
======= =======
Copyright 2010 - 2013 Sami Samhuri sami@samhuri.net Copyright 2010 - 2015 Sami Samhuri sami@samhuri.net
[MIT license](http://sjs.mit-license.org) [MIT license](http://sjs.mit-license.org)

13
bower.json Normal file
View file

@ -0,0 +1,13 @@
{
"name": "strftime",
"version": "0.9.2",
"main": "strftime.js",
"ignore": [
"Readme.md",
"Makefile",
"test",
"*.json"
],
"dependencies": {},
"devDependencies": {}
}

View file

@ -3,7 +3,7 @@
"repo": "samsonjs/strftime", "repo": "samsonjs/strftime",
"description": "strftime date formatting", "description": "strftime date formatting",
"keywords": ["strftime", "format", "date", "time"], "keywords": ["strftime", "format", "date", "time"],
"version": "0.8.2", "version": "0.9.2",
"main": "strftime.js", "main": "strftime.js",
"scripts": ["strftime.js"] "scripts": ["strftime.js"]
} }

View file

@ -1,14 +1,20 @@
{ {
"name": "strftime", "name": "strftime",
"description": "strftime for JavaScript", "description": "strftime for JavaScript",
"version": "0.8.2", "version": "0.9.2",
"homepage": "http://samhuri.net/proj/strftime", "homepage": "http://samhuri.net/proj/strftime",
"author": "Sami Samhuri <sami@samhuri.net>", "author": "Sami Samhuri <sami@samhuri.net>",
"contributors": [ "contributors": [
"Sami Samhuri <sami@samhuri.net> (http://samhuri.net)", "Sami Samhuri <sami@samhuri.net> (http://samhuri.net)",
"Andrew Schaaf <andrew@andrewschaaf.com> (http://andrewschaaf.com/)", "Andrew Schaaf <andrew@andrewschaaf.com> (http://andrewschaaf.com/)",
"Rob Colburn <rob@robcolburn.com> (http://robcolburn.com/)", "Rob Colburn <rob@robcolburn.com> (http://robcolburn.com/)",
"Ryan Stafford (http://droffats.net/)" "Ryan Stafford (http://droffats.net/)",
"Alexandr Nikitin <nikitin.alexandr.a@gmail.com>",
"TJ Holowaychuk (http://tjholowaychuk.com/)",
"John Zwinck (https://github.com/jzwinck)",
"Forbes Lindesay (https://github.com/ForbesLindesay)",
"Ryan Regalado (https://github.com/d48)",
"w0den (https://github.com/w0den)"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
@ -22,12 +28,7 @@
"engines": { "engines": {
"node": ">=0.2.0" "node": ">=0.2.0"
}, },
"licenses": [ "license": "MIT",
{
"type": "MIT",
"url": "http://sjs.mit-license.org"
}
],
"dependencies": {}, "dependencies": {},
"devDependencies": {}, "devDependencies": {},
"optionalDependencies": {} "optionalDependencies": {}

20
strftime-min.js vendored
View file

@ -1,8 +1,12 @@
(function(){function i(c,a,b){return g(c,a,b)}function g(c,a,b,j){j=j||{};a&&!n(a)&&(b=a,a=void 0);a=a||new Date;b=b||o;b.formats=b.formats||{};var i=a.getTime(),h=j.timezone,e=typeof h;if(j.utc||e=="number"||e=="string")a=p(a);if(h){if(e=="string")var k=h[0]=="-"?-1:1,q=parseInt(h.slice(1,3),10),r=parseInt(h.slice(3,5),10),h=k*(60*q+r);e&&(a=new Date(a.getTime()+h*6E4))}return c.replace(/%([-_0]?.)/g,function(c,e){var d;if(e.length==2){d=e[0];if(d=="-")d="";else if(d=="_")d=" ";else if(d=="0")d= (function(){function k(b,a){s[b]||(typeof console!=="undefined"&&typeof console.warn=="function"&&console.warn("[WARNING] "+b+" is deprecated and will be removed in version 1.0. Instead, use `"+a+"`."),s[b]=!0)}function t(b){b.localize=i.localize.bind(i);b.timezone=i.timezone.bind(i);b.utc=i.utc.bind(i)}function r(b,a,e){a&&a.days&&(e=a,a=void 0);e&&k("`"+g+"(format, [date], [locale])`","var s = "+g+".localize(locale); s(format, [date])");return(e?i.localize(e):i)(b,a)}function u(b,a,e){e?k("`"+g+
"0";else return c;e=e[1]}switch(e){case "A":return b.days[a.getDay()];case "a":return b.shortDays[a.getDay()];case "B":return b.months[a.getMonth()];case "b":return b.shortMonths[a.getMonth()];case "C":return f(Math.floor(a.getFullYear()/100),d);case "D":return g(b.formats.D||"%m/%d/%y",a,b);case "d":return f(a.getDate(),d);case "e":return f(a.getDate(),d==null?" ":d);case "F":return g(b.formats.F||"%Y-%m-%d",a,b);case "H":return f(a.getHours(),d);case "h":return b.shortMonths[a.getMonth()];case "I":return f(l(a), ".strftime(format, [date], [locale])`","var s = "+g+".localize(locale); s(format, [date])"):k("`"+g+".strftime(format, [date])`",g+"(format, [date])");return(e?i.localize(e):i)(b,a)}function p(b,a,e){function g(b,c,h,a){for(var d="",f=null,e=!1,i=b.length,j=!1,o=0;o<i;o++){var n=b.charCodeAt(o);if(e===!0)if(n===45)f="";else if(n===95)f=" ";else if(n===48)f="0";else if(n===58)j&&typeof console!=="undefined"&&typeof console.warn=="function"&&console.warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime"),
d);case "j":return d=new Date(a.getFullYear(),0,1),d=Math.ceil((a.getTime()-d.getTime())/864E5),f(d,3);case "k":return f(a.getHours(),d==null?" ":d);case "L":return f(Math.floor(i%1E3),3);case "l":return f(l(a),d==null?" ":d);case "M":return f(a.getMinutes(),d);case "m":return f(a.getMonth()+1,d);case "n":return"\n";case "o":return String(a.getDate())+s(a.getDate());case "P":return a.getHours()<12?b.am:b.pm;case "p":return a.getHours()<12?b.AM:b.PM;case "R":return g(b.formats.R||"%H:%M",a,b);case "r":return g(b.formats.r|| j=!0;else{switch(n){case 65:d+=h.days[c.getDay()];break;case 66:d+=h.months[c.getMonth()];break;case 67:d+=l(Math.floor(c.getFullYear()/100),f);break;case 68:d+=g(h.formats.D,c,h,a);break;case 70:d+=g(h.formats.F,c,h,a);break;case 72:d+=l(c.getHours(),f);break;case 73:d+=l(v(c.getHours()),f);break;case 76:d+=Math.floor(a%1E3)>99?Math.floor(a%1E3):Math.floor(a%1E3)>9?"0"+Math.floor(a%1E3):"00"+Math.floor(a%1E3);break;case 77:d+=l(c.getMinutes(),f);break;case 80:d+=c.getHours()<12?h.am:h.pm;break;case 82:d+=
"%I:%M:%S %p",a,b);case "S":return f(a.getSeconds(),d);case "s":return Math.floor(i/1E3);case "T":return g(b.formats.T||"%H:%M:%S",a,b);case "t":return"\t";case "U":return f(m(a,"sunday"),d);case "u":return d=a.getDay(),d==0?7:d;case "v":return g(b.formats.v||"%e-%b-%Y",a,b);case "W":return f(m(a,"monday"),d);case "w":return a.getDay();case "Y":return a.getFullYear();case "y":return d=String(a.getFullYear()),d.slice(d.length-2);case "Z":return j.utc?"GMT":(d=a.toString().match(/\(([\w\s]+)\)/))&& g(h.formats.R,c,h,a);break;case 83:d+=l(c.getSeconds(),f);break;case 84:d+=g(h.formats.T,c,h,a);break;case 85:d+=l(w(c,"sunday"),f);break;case 87:d+=l(w(c,"monday"),f);break;case 88:d+=g(h.formats.X,c,h,a);break;case 89:d+=c.getFullYear();break;case 90:k&&m===0?d+="GMT":(f=c.toString().match(/\(([\w\s]+)\)/),d+=f&&f[1]||"");break;case 97:d+=h.shortDays[c.getDay()];break;case 98:d+=h.shortMonths[c.getMonth()];break;case 99:d+=g(h.formats.c,c,h,a);break;case 100:d+=l(c.getDate(),f);break;case 101:d+=
d[1]||"";case "z":return j.utc?"+0000":(d=typeof h=="number"?h:-a.getTimezoneOffset(),(d<0?"-":"+")+f(Math.floor(Math.abs(d)/60))+f(Math.abs(d)%60));default:return e}})}function p(c){var a=(c.getTimezoneOffset()||0)*6E4;return new Date(c.getTime()+a)}function n(c){for(var a=0,b=k.length,a=0;a<b;++a)if(typeof c[k[a]]!="function")return!1;return!0}function f(c,a,b){typeof a==="number"&&(b=a,a="0");a==null&&(a="0");b=b||2;c=String(c);if(a)for(;c.length<b;)c=a+c;return c}function l(c){c=c.getHours(); l(c.getDate(),f==null?" ":f);break;case 104:d+=h.shortMonths[c.getMonth()];break;case 106:f=new Date(c.getFullYear(),0,1);f=Math.ceil((c.getTime()-f.getTime())/864E5);d+=f>99?f:f>9?"0"+f:"00"+f;break;case 107:d+=l(c.getHours(),f==null?" ":f);break;case 108:d+=l(v(c.getHours()),f==null?" ":f);break;case 109:d+=l(c.getMonth()+1,f);break;case 110:d+="\n";break;case 111:d+=String(c.getDate())+A(c.getDate());break;case 112:d+=c.getHours()<12?h.AM:h.PM;break;case 114:d+=g(h.formats.r,c,h,a);break;case 115:d+=
c==0?c=12:c>12&&(c-=12);return c}function s(c){var a=c%10;c%=100;if(c>=11&&c<=13||a===0||a>=4)return"th";switch(a){case 1:return"st";case 2:return"nd";case 3:return"rd"}}function m(c,a){var a=a||"sunday",b=c.getDay();a=="monday"&&(b==0?b=6:b--);var e=new Date(c.getFullYear(),0,1);return Math.floor(((c-e)/864E5+7-b)/7)}var e;e=typeof module!=="undefined"?module.exports=i:function(){return this||(0,eval)("this")}();var o={days:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),shortDays:"Sun Mon Tue Wed Thu Fri Sat".split(" "), Math.floor(a/1E3);break;case 116:d+="\t";break;case 117:f=c.getDay();d+=f===0?7:f;break;case 118:d+=g(h.formats.v,c,h,a);break;case 119:d+=c.getDay();break;case 120:d+=g(h.formats.x,c,h,a);break;case 121:d+=(""+c.getFullYear()).slice(2);break;case 122:k&&m===0?d+=j?"+00:00":"+0000":(f=m!==0?m/6E4:-c.getTimezoneOffset(),e=j?":":"",n=Math.abs(f%60),d+=(f<0?"-":"+")+l(Math.floor(Math.abs(f/60)))+e+l(n));break;default:d+=b[o]}f=null;e=!1}else n===37?e=!0:d+=b[o]}return d}var i=b||x,m=a||0,k=e||!1,j=0,
months:"January February March April May June July August September October November December".split(" "),shortMonths:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),AM:"AM",PM:"PM",am:"am",pm:"pm"};e.strftime=i;e.strftimeTZ=i.strftimeTZ=function(c,a,b,e){if((typeof b=="number"||typeof b=="string")&&e==null)e=b,b=void 0;return g(c,a,b,{timezone:e})};e.strftimeUTC=i.strftimeUTC=function(c,a,b){return g(c,a,b,{utc:!0})};e.localizedStrftime=i.localizedStrftime=function(c){return function(a, q,b=function(b,c){var a;c?(a=c.getTime(),k&&(c=new Date(c.getTime()+(c.getTimezoneOffset()||0)*6E4+m))):(a=Date.now(),a>j?(j=a,q=new Date(j),a=j,k&&(q=new Date(j+(q.getTimezoneOffset()||0)*6E4+m))):a=j,c=q);return g(b,c,i,a)};b.localize=function(a){return new p(a||i,m,k)};b.timezone=function(a){var c=m,b=k,e=typeof a;if(e==="number"||e==="string")b=!0,e==="string"?(c=a[0]==="-"?-1:1,e=parseInt(a.slice(1,3),10),a=parseInt(a.slice(3,5),10),c=c*(60*e+a)*6E4):e==="number"&&(c=a*6E4);return new p(i,c,
b){return g(a,b,c)}};var k=["getTime","getTimezoneOffset","getDay","getDate","getMonth","getFullYear","getYear","getHours","getMinutes","getSeconds"]})(); b)};b.utc=function(){return new p(i,m,!0)};return b}function l(b,a){if(a===""||b>9)return b;a==null&&(a="0");return a+b}function v(b){if(b===0)return 12;else if(b>12)return b-12;return b}function w(b,a){var a=a||"sunday",e=b.getDay();a==="monday"&&(e===0?e=6:e--);var g=Date.UTC(b.getFullYear(),0,1),i=Date.UTC(b.getFullYear(),b.getMonth(),b.getDate());return Math.floor((Math.floor((i-g)/864E5)+7-e)/7)}function A(b){var a=b%10;b%=100;if(b>=11&&b<=13||a===0||a>=4)return"th";switch(a){case 1:return"st";
case 2:return"nd";case 3:return"rd"}}var x={days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],AM:"AM",PM:"PM",am:"am",pm:"pm",formats:{D:"%m/%d/%y",F:"%Y-%m-%d",R:"%H:%M",T:"%H:%M:%S",X:"%T",c:"%a %b %d %X %Y",r:"%I:%M:%S %p",
v:"%e-%b-%Y",x:"%D"}},i=new p(x,0,!1),y=typeof module!=="undefined",j;y?(j=module.exports=r,j.strftime=u):(j=function(){return this||(0,eval)("this")}(),j.strftime=r);var g=y?"require('strftime')":"strftime",s={};j.strftimeTZ=function(b,a,e,j){if((typeof e=="number"||typeof e=="string")&&j==null)j=e,e=void 0;e?k("`"+g+".strftimeTZ(format, date, locale, tz)`","var s = "+g+".localize(locale).timezone(tz); s(format, [date])` or `var s = "+g+".localize(locale); s.timezone(tz)(format, [date])"):k("`"+
g+".strftimeTZ(format, date, tz)`","var s = "+g+".timezone(tz); s(format, [date])` or `"+g+".timezone(tz)(format, [date])");return(e?i.localize(e):i).timezone(j)(b,a)};j.strftimeUTC=function(b,a,e){e?k("`"+g+".strftimeUTC(format, date, locale)`","var s = "+g+".localize(locale).utc(); s(format, [date])"):k("`"+g+".strftimeUTC(format, [date])`","var s = "+g+".utc(); s(format, [date])");return(e?z.localize(e):z)(b,a)};j.localizedStrftime=function(b){k("`"+g+".localizedStrftime(locale)`",g+".localize(locale)");
return i.localize(b)};t(r);t(u);var z=i.utc();if(typeof Date.now!=="function")Date.now=function(){return+new Date}})();

View file

@ -3,7 +3,7 @@
// github.com/samsonjs/strftime // github.com/samsonjs/strftime
// @_sjs // @_sjs
// //
// Copyright 2010 - 2013 Sami Samhuri <sami@samhuri.net> // Copyright 2010 - 2015 Sami Samhuri <sami@samhuri.net>
// //
// MIT License // MIT License
// http://sjs.mit-license.org // http://sjs.mit-license.org
@ -11,326 +11,607 @@
;(function() { ;(function() {
//// Where to export the API var DefaultLocale = {
var namespace; days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
AM: 'AM',
PM: 'PM',
am: 'am',
pm: 'pm',
formats: {
D: '%m/%d/%y',
F: '%Y-%m-%d',
R: '%H:%M',
T: '%H:%M:%S',
X: '%T',
c: '%a %b %d %X %Y',
r: '%I:%M:%S %p',
v: '%e-%b-%Y',
x: '%D'
}
},
defaultStrftime = new Strftime(DefaultLocale, 0, false),
isCommonJS = typeof module !== 'undefined',
namespace;
// CommonJS / Node module // CommonJS / Node module
if (typeof module !== 'undefined') { if (isCommonJS) {
namespace = module.exports = strftime; namespace = module.exports = adaptedStrftime;
namespace.strftime = deprecatedStrftime;
} }
// Browsers and other environments // Browsers and other environments
else { else {
// Get the global object. Works in ES3, ES5, and ES5 strict mode. // Get the global object. Works in ES3, ES5, and ES5 strict mode.
namespace = (function(){ return this || (1,eval)('this') }()); namespace = (function() { return this || (1,eval)('this'); }());
namespace.strftime = adaptedStrftime;
} }
function words(s) { return (s || '').split(' '); } // Deprecated API, to be removed in v1.0
var _require = isCommonJS ? "require('strftime')" : "strftime";
var DefaultLocale = var _deprecationWarnings = {};
{ days: words('Sunday Monday Tuesday Wednesday Thursday Friday Saturday') function deprecationWarning(name, instead) {
, shortDays: words('Sun Mon Tue Wed Thu Fri Sat') if (!_deprecationWarnings[name]) {
, months: words('January February March April May June July August September October November December') if (typeof console !== 'undefined' && typeof console.warn == 'function') {
, shortMonths: words('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec') console.warn("[WARNING] " + name + " is deprecated and will be removed in version 1.0. Instead, use `" + instead + "`.");
, AM: 'AM' }
, PM: 'PM' _deprecationWarnings[name] = true;
, am: 'am' }
, pm: 'pm'
};
namespace.strftime = strftime;
function strftime(fmt, d, locale) {
return _strftime(fmt, d, locale);
} }
// locale is optional namespace.strftimeTZ = deprecatedStrftimeTZ;
namespace.strftimeTZ = strftime.strftimeTZ = strftimeTZ; namespace.strftimeUTC = deprecatedStrftimeUTC;
function strftimeTZ(fmt, d, locale, timezone) { namespace.localizedStrftime = deprecatedStrftimeLocalized;
// Adapt the old API while preserving the new API.
function adaptForwards(fn) {
fn.localize = defaultStrftime.localize.bind(defaultStrftime);
fn.timezone = defaultStrftime.timezone.bind(defaultStrftime);
fn.utc = defaultStrftime.utc.bind(defaultStrftime);
}
adaptForwards(adaptedStrftime);
function adaptedStrftime(fmt, d, locale) {
// d and locale are optional, check if this is (format, locale)
if (d && d.days) {
locale = d;
d = undefined;
}
if (locale) {
deprecationWarning("`" + _require + "(format, [date], [locale])`", "var s = " + _require + ".localize(locale); s(format, [date])");
}
var strftime = locale ? defaultStrftime.localize(locale) : defaultStrftime;
return strftime(fmt, d);
}
adaptForwards(deprecatedStrftime);
function deprecatedStrftime(fmt, d, locale) {
if (locale) {
deprecationWarning("`" + _require + ".strftime(format, [date], [locale])`", "var s = " + _require + ".localize(locale); s(format, [date])");
}
else {
deprecationWarning("`" + _require + ".strftime(format, [date])`", _require + "(format, [date])");
}
var strftime = locale ? defaultStrftime.localize(locale) : defaultStrftime;
return strftime(fmt, d);
}
function deprecatedStrftimeTZ(fmt, d, locale, timezone) {
// locale is optional, check if this is (format, date, timezone)
if ((typeof locale == 'number' || typeof locale == 'string') && timezone == null) { if ((typeof locale == 'number' || typeof locale == 'string') && timezone == null) {
timezone = locale; timezone = locale;
locale = undefined; locale = undefined;
} }
return _strftime(fmt, d, locale, { timezone: timezone });
if (locale) {
deprecationWarning("`" + _require + ".strftimeTZ(format, date, locale, tz)`", "var s = " + _require + ".localize(locale).timezone(tz); s(format, [date])` or `var s = " + _require + ".localize(locale); s.timezone(tz)(format, [date])");
}
else {
deprecationWarning("`" + _require + ".strftimeTZ(format, date, tz)`", "var s = " + _require + ".timezone(tz); s(format, [date])` or `" + _require + ".timezone(tz)(format, [date])");
} }
namespace.strftimeUTC = strftime.strftimeUTC = strftimeUTC; var strftime = (locale ? defaultStrftime.localize(locale) : defaultStrftime).timezone(timezone);
function strftimeUTC(fmt, d, locale) { return strftime(fmt, d);
return _strftime(fmt, d, locale, { utc: true });
} }
namespace.localizedStrftime = strftime.localizedStrftime = localizedStrftime; var utcStrftime = defaultStrftime.utc();
function localizedStrftime(locale) { function deprecatedStrftimeUTC(fmt, d, locale) {
return function(fmt, d, options) { if (locale) {
return strftime(fmt, d, locale, options); deprecationWarning("`" + _require + ".strftimeUTC(format, date, locale)`", "var s = " + _require + ".localize(locale).utc(); s(format, [date])");
}
else {
deprecationWarning("`" + _require + ".strftimeUTC(format, [date])`", "var s = " + _require + ".utc(); s(format, [date])");
}
var strftime = locale ? utcStrftime.localize(locale) : utcStrftime;
return strftime(fmt, d);
}
function deprecatedStrftimeLocalized(locale) {
deprecationWarning("`" + _require + ".localizedStrftime(locale)`", _require + ".localize(locale)");
return defaultStrftime.localize(locale);
}
// End of deprecated API
// Polyfill Date.now for old browsers.
if (typeof Date.now !== 'function') {
Date.now = function() {
return +new Date();
}; };
} }
// d, locale, and options are optional, but you can't leave function Strftime(locale, customTimezoneOffset, useUtcTimezone) {
// holes in the argument list. If you pass options you have to pass var _locale = locale || DefaultLocale,
// in all the preceding args as well. _customTimezoneOffset = customTimezoneOffset || 0,
// _useUtcBasedDate = useUtcTimezone || false,
// options:
// - locale [object] an object with the same structure as DefaultLocale
// - timezone [number] timezone offset in minutes from GMT
function _strftime(fmt, d, locale, options) {
options = options || {};
// d and locale are optional so check if d is really the locale // we store unix timestamp value here to not create new Date() each iteration (each millisecond)
if (d && !quacksLikeDate(d)) { // Date.now() is 2 times faster than new Date()
locale = d; // while millisecond precise is enough here
d = undefined; // this could be very helpful when strftime triggered a lot of times one by one
_cachedDateTimestamp = 0,
_cachedDate;
function _strftime(format, date) {
var timestamp;
if (!date) {
var currentTimestamp = Date.now();
if (currentTimestamp > _cachedDateTimestamp) {
_cachedDateTimestamp = currentTimestamp;
_cachedDate = new Date(_cachedDateTimestamp);
timestamp = _cachedDateTimestamp;
if (_useUtcBasedDate) {
// how to avoid duplication of date instantiation for utc here?
// we tied to getTimezoneOffset of the current date
_cachedDate = new Date(_cachedDateTimestamp + getTimestampToUtcOffsetFor(_cachedDate) + _customTimezoneOffset);
} }
d = d || new Date();
locale = locale || DefaultLocale;
locale.formats = locale.formats || {};
// Hang on to this Unix timestamp because we might mess with it directly below.
var timestamp = d.getTime();
var tz = options.timezone;
var tzType = typeof tz;
if (options.utc || tzType == 'number' || tzType == 'string') {
d = dateToUTC(d);
}
if (tz) {
// ISO 8601 format timezone string, [-+]HHMM
//
// Convert to the number of minutes and it'll be applied to the date below.
if (tzType == 'string') {
var sign = tz[0] == '-' ? -1 : 1;
var hours = parseInt(tz.slice(1, 3), 10);
var mins = parseInt(tz.slice(3, 5), 10);
tz = sign * ((60 * hours) + mins);
}
if (tzType) {
d = new Date(d.getTime() + (tz * 60000));
}
}
// Most of the specifiers supported by C's strftime, and some from Ruby.
// Some other syntax extensions from Ruby are supported: %-, %_, and %0
// to pad with nothing, space, or zero (respectively).
return fmt.replace(/%([-_0]?.)/g, function(_, c) {
var mod, padding;
if (c.length == 2) {
mod = c[0];
// omit padding
if (mod == '-') {
padding = '';
}
// pad with space
else if (mod == '_') {
padding = ' ';
}
// pad with zero
else if (mod == '0') {
padding = '0';
} }
else { else {
// unrecognized, return the format timestamp = _cachedDateTimestamp;
return _; }
date = _cachedDate;
}
else {
timestamp = date.getTime();
if (_useUtcBasedDate) {
date = new Date(date.getTime() + getTimestampToUtcOffsetFor(date) + _customTimezoneOffset);
} }
c = c[1];
} }
switch (c) { return _processFormat(format, date, _locale, timestamp);
}
function _processFormat(format, date, locale, timestamp) {
var resultString = '',
padding = null,
isInScope = false,
length = format.length,
extendedTZ = false;
for (var i = 0; i < length; i++) {
var currentCharCode = format.charCodeAt(i);
if (isInScope === true) {
// '-'
if (currentCharCode === 45) {
padding = '';
continue;
}
// '_'
else if (currentCharCode === 95) {
padding = ' ';
continue;
}
// '0'
else if (currentCharCode === 48) {
padding = '0';
continue;
}
// ':'
else if (currentCharCode === 58) {
if (extendedTZ) {
if (typeof console !== 'undefined' && typeof console.warn == 'function') {
console.warn("[WARNING] detected use of unsupported %:: or %::: modifiers to strftime");
}
}
extendedTZ = true;
continue;
}
switch (currentCharCode) {
// Examples for new Date(0) in GMT // Examples for new Date(0) in GMT
// 'Thursday' // 'Thursday'
case 'A': return locale.days[d.getDay()]; // case 'A':
case 65:
// 'Thu' resultString += locale.days[date.getDay()];
case 'a': return locale.shortDays[d.getDay()]; break;
// 'January' // 'January'
case 'B': return locale.months[d.getMonth()]; // case 'B':
case 66:
// 'Jan' resultString += locale.months[date.getMonth()];
case 'b': return locale.shortMonths[d.getMonth()]; break;
// '19' // '19'
case 'C': return pad(Math.floor(d.getFullYear() / 100), padding); // case 'C':
case 67:
resultString += padTill2(Math.floor(date.getFullYear() / 100), padding);
break;
// '01/01/70' // '01/01/70'
case 'D': return _strftime(locale.formats.D || '%m/%d/%y', d, locale); // case 'D':
case 68:
// '01' resultString += _processFormat(locale.formats.D, date, locale, timestamp);
case 'd': return pad(d.getDate(), padding); break;
// '01'
case 'e': return pad(d.getDate(), padding == null ? ' ' : padding);
// '1970-01-01' // '1970-01-01'
case 'F': return _strftime(locale.formats.F || '%Y-%m-%d', d, locale); // case 'F':
case 70:
resultString += _processFormat(locale.formats.F, date, locale, timestamp);
break;
// '00' // '00'
case 'H': return pad(d.getHours(), padding); // case 'H':
case 72:
// 'Jan' resultString += padTill2(date.getHours(), padding);
case 'h': return locale.shortMonths[d.getMonth()]; break;
// '12' // '12'
case 'I': return pad(hours12(d), padding); // case 'I':
case 73:
resultString += padTill2(hours12(date.getHours()), padding);
break;
// '000' // '000'
case 'j': // case 'L':
var y = new Date(d.getFullYear(), 0, 1); case 76:
var day = Math.ceil((d.getTime() - y.getTime()) / (1000 * 60 * 60 * 24)); resultString += padTill3(Math.floor(timestamp % 1000));
return pad(day, 3); break;
// ' 0'
case 'k': return pad(d.getHours(), padding == null ? ' ' : padding);
// '000'
case 'L': return pad(Math.floor(timestamp % 1000), 3);
// '12'
case 'l': return pad(hours12(d), padding == null ? ' ' : padding);
// '00' // '00'
case 'M': return pad(d.getMinutes(), padding); // case 'M':
case 77:
// '01' resultString += padTill2(date.getMinutes(), padding);
case 'm': return pad(d.getMonth() + 1, padding); break;
// '\n'
case 'n': return '\n';
// '1st'
case 'o': return String(d.getDate()) + ordinal(d.getDate());
// 'am' // 'am'
case 'P': return d.getHours() < 12 ? locale.am : locale.pm; // case 'P':
case 80:
// 'AM' resultString += date.getHours() < 12 ? locale.am : locale.pm;
case 'p': return d.getHours() < 12 ? locale.AM : locale.PM; break;
// '00:00' // '00:00'
case 'R': return _strftime(locale.formats.R || '%H:%M', d, locale); // case 'R':
case 82:
// '12:00:00 AM' resultString += _processFormat(locale.formats.R, date, locale, timestamp);
case 'r': return _strftime(locale.formats.r || '%I:%M:%S %p', d, locale); break;
// '00' // '00'
case 'S': return pad(d.getSeconds(), padding); // case 'S':
case 83:
// '0' resultString += padTill2(date.getSeconds(), padding);
case 's': return Math.floor(timestamp / 1000); break;
// '00:00:00' // '00:00:00'
case 'T': return _strftime(locale.formats.T || '%H:%M:%S', d, locale); // case 'T':
case 84:
// '\t' resultString += _processFormat(locale.formats.T, date, locale, timestamp);
case 't': return '\t'; break;
// '00' // '00'
case 'U': return pad(weekNumber(d, 'sunday'), padding); // case 'U':
case 85:
// '4' resultString += padTill2(weekNumber(date, 'sunday'), padding);
case 'u': break;
var day = d.getDay();
return day == 0 ? 7 : day; // 1 - 7, Monday is first day of the week
// ' 1-Jan-1970'
case 'v': return _strftime(locale.formats.v || '%e-%b-%Y', d, locale);
// '00' // '00'
case 'W': return pad(weekNumber(d, 'monday'), padding); // case 'W':
case 87:
resultString += padTill2(weekNumber(date, 'monday'), padding);
break;
// '4' // '16:00:00'
case 'w': return d.getDay(); // 0 - 6, Sunday is first day of the week // case 'X':
case 88:
resultString += _processFormat(locale.formats.X, date, locale, timestamp);
break;
// '1970' // '1970'
case 'Y': return d.getFullYear(); // case 'Y':
case 89:
// '70' resultString += date.getFullYear();
case 'y': break;
var y = String(d.getFullYear());
return y.slice(y.length - 2);
// 'GMT' // 'GMT'
case 'Z': // case 'Z':
if (options.utc) { case 90:
return "GMT"; if (_useUtcBasedDate && _customTimezoneOffset === 0) {
resultString += "GMT";
} }
else { else {
var tzString = d.toString().match(/\(([\w\s]+)\)/); // fixme optimize
return tzString && tzString[1] || ''; var tzString = date.toString().match(/\(([\w\s]+)\)/);
resultString += tzString && tzString[1] || '';
} }
break;
// 'Thu'
// case 'a':
case 97:
resultString += locale.shortDays[date.getDay()];
break;
// 'Jan'
// case 'b':
case 98:
resultString += locale.shortMonths[date.getMonth()];
break;
// ''
// case 'c':
case 99:
resultString += _processFormat(locale.formats.c, date, locale, timestamp);
break;
// '01'
// case 'd':
case 100:
resultString += padTill2(date.getDate(), padding);
break;
// ' 1'
// case 'e':
case 101:
resultString += padTill2(date.getDate(), padding == null ? ' ' : padding);
break;
// 'Jan'
// case 'h':
case 104:
resultString += locale.shortMonths[date.getMonth()];
break;
// '000'
// case 'j':
case 106:
var y = new Date(date.getFullYear(), 0, 1);
var day = Math.ceil((date.getTime() - y.getTime()) / (1000 * 60 * 60 * 24));
resultString += padTill3(day);
break;
// ' 0'
// case 'k':
case 107:
resultString += padTill2(date.getHours(), padding == null ? ' ' : padding);
break;
// '12'
// case 'l':
case 108:
resultString += padTill2(hours12(date.getHours()), padding == null ? ' ' : padding);
break;
// '01'
// case 'm':
case 109:
resultString += padTill2(date.getMonth() + 1, padding);
break;
// '\n'
// case 'n':
case 110:
resultString += '\n';
break;
// '1st'
// case 'o':
case 111:
resultString += String(date.getDate()) + ordinal(date.getDate());
break;
// 'AM'
// case 'p':
case 112:
resultString += date.getHours() < 12 ? locale.AM : locale.PM;
break;
// '12:00:00 AM'
// case 'r':
case 114:
resultString += _processFormat(locale.formats.r, date, locale, timestamp);
break;
// '0'
// case 's':
case 115:
resultString += Math.floor(timestamp / 1000);
break;
// '\t'
// case 't':
case 116:
resultString += '\t';
break;
// '4'
// case 'u':
case 117:
var day = date.getDay();
resultString += day === 0 ? 7 : day;
break; // 1 - 7, Monday is first day of the week
// ' 1-Jan-1970'
// case 'v':
case 118:
resultString += _processFormat(locale.formats.v, date, locale, timestamp);
break;
// '4'
// case 'w':
case 119:
resultString += date.getDay();
break; // 0 - 6, Sunday is first day of the week
// '12/31/69'
// case 'x':
case 120:
resultString += _processFormat(locale.formats.x, date, locale, timestamp);
break;
// '70'
// case 'y':
case 121:
resultString += ('' + date.getFullYear()).slice(2);
break;
// '+0000' // '+0000'
case 'z': // case 'z':
if (options.utc) { case 122:
return "+0000"; if (_useUtcBasedDate && _customTimezoneOffset === 0) {
resultString += extendedTZ ? "+00:00" : "+0000";
} }
else { else {
var off = typeof tz == 'number' ? tz : -d.getTimezoneOffset(); var off;
return (off < 0 ? '-' : '+') + pad(Math.floor(Math.abs(off) / 60)) + pad(Math.abs(off) % 60); if (_customTimezoneOffset !== 0) {
off = _customTimezoneOffset / (60 * 1000);
}
else {
off = -date.getTimezoneOffset();
}
var sign = off < 0 ? '-' : '+';
var sep = extendedTZ ? ':' : '';
var hours = Math.floor(Math.abs(off / 60));
var mins = Math.abs(off % 60);
resultString += sign + padTill2(hours) + sep + padTill2(mins);
}
break;
default:
resultString += format[i];
break;
} }
default: return c; padding = null;
} isInScope = false;
}); continue;
} }
function dateToUTC(d) { // '%'
var msDelta = (d.getTimezoneOffset() || 0) * 60000; if (currentCharCode === 37) {
return new Date(d.getTime() + msDelta); isInScope = true;
continue;
} }
var RequiredDateMethods = ['getTime', 'getTimezoneOffset', 'getDay', 'getDate', 'getMonth', 'getFullYear', 'getYear', 'getHours', 'getMinutes', 'getSeconds']; resultString += format[i];
function quacksLikeDate(x) {
var i = 0
, n = RequiredDateMethods.length
;
for (i = 0; i < n; ++i) {
if (typeof x[RequiredDateMethods[i]] != 'function') {
return false;
}
}
return true;
} }
// Default padding is '0' and default length is 2, both are optional. return resultString;
function pad(n, padding, length) {
// pad(n, <length>)
if (typeof padding === 'number') {
length = padding;
padding = '0';
} }
// Defaults handle pad(n) and pad(n, <padding>) var strftime = _strftime;
if (padding == null) {
padding = '0';
}
length = length || 2;
var s = String(n); strftime.localize = function(locale) {
// padding may be an empty string, don't loop forever if it is return new Strftime(locale || _locale, _customTimezoneOffset, _useUtcBasedDate);
if (padding) { };
while (s.length < length) s = padding + s;
strftime.timezone = function(timezone) {
var customTimezoneOffset = _customTimezoneOffset;
var useUtcBasedDate = _useUtcBasedDate;
var timezoneType = typeof timezone;
if (timezoneType === 'number' || timezoneType === 'string') {
useUtcBasedDate = true;
// ISO 8601 format timezone string, [-+]HHMM
if (timezoneType === 'string') {
var sign = timezone[0] === '-' ? -1 : 1,
hours = parseInt(timezone.slice(1, 3), 10),
minutes = parseInt(timezone.slice(3, 5), 10);
customTimezoneOffset = sign * ((60 * hours) + minutes) * 60 * 1000;
// in minutes: 420
}
else if (timezoneType === 'number') {
customTimezoneOffset = timezone * 60 * 1000;
} }
return s;
} }
function hours12(d) { return new Strftime(_locale, customTimezoneOffset, useUtcBasedDate);
var hour = d.getHours(); };
if (hour == 0) hour = 12;
else if (hour > 12) hour -= 12; strftime.utc = function() {
return new Strftime(_locale, _customTimezoneOffset, true);
};
return strftime;
}
function padTill2(numberToPad, paddingChar) {
if (paddingChar === '' || numberToPad > 9) {
return numberToPad;
}
if (paddingChar == null) {
paddingChar = '0';
}
return paddingChar + numberToPad;
}
function padTill3(numberToPad) {
if (numberToPad > 99) {
return numberToPad;
}
if (numberToPad > 9) {
return '0' + numberToPad;
}
return '00' + numberToPad;
}
function hours12(hour) {
if (hour === 0) {
return 12;
}
else if (hour > 12) {
return hour - 12;
}
return hour; return hour;
} }
// firstWeekday: 'sunday' or 'monday', default is 'sunday'
//
// Pilfered & ported from Ruby's strftime implementation.
function weekNumber(date, firstWeekday) {
firstWeekday = firstWeekday || 'sunday';
// This works by shifting the weekday back by one day if we
// are treating Monday as the first day of the week.
var weekday = date.getDay();
if (firstWeekday === 'monday') {
if (weekday === 0) // Sunday
weekday = 6;
else
weekday--;
}
var firstDayOfYearUtc = Date.UTC(date.getFullYear(), 0, 1),
dateUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
yday = Math.floor((dateUtc - firstDayOfYearUtc) / 86400000),
weekNum = (yday + 7 - weekday) / 7;
return Math.floor(weekNum);
}
// Get the ordinal suffix for a number: st, nd, rd, or th // Get the ordinal suffix for a number: st, nd, rd, or th
function ordinal(n) { function ordinal(number) {
var i = n % 10 var i = number % 10;
, ii = n % 100 var ii = number % 100;
;
if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) { if ((ii >= 11 && ii <= 13) || i === 0 || i >= 4) {
return 'th'; return 'th';
} }
@ -341,26 +622,8 @@
} }
} }
// firstWeekday: 'sunday' or 'monday', default is 'sunday' function getTimestampToUtcOffsetFor(date) {
// return (date.getTimezoneOffset() || 0) * 60000;
// Pilfered & ported from Ruby's strftime implementation.
function weekNumber(d, firstWeekday) {
firstWeekday = firstWeekday || 'sunday';
// This works by shifting the weekday back by one day if we
// are treating Monday as the first day of the week.
var wday = d.getDay();
if (firstWeekday == 'monday') {
if (wday == 0) // Sunday
wday = 6;
else
wday--;
}
var firstDayOfYear = new Date(d.getFullYear(), 0, 1)
, yday = (d - firstDayOfYear) / 86400000
, weekNum = (yday + 7 - wday) / 7
;
return Math.floor(weekNum);
} }
}()); }());

263
test.js Executable file
View file

@ -0,0 +1,263 @@
#!/usr/bin/env node
// Based on CoffeeScript by andrewschaaf on github
//
// TODO:
// - past and future dates, especially < 1900 and > 2100
// - look for edge cases
var assert = require('assert'),
libFilename = process.argv[2] || './strftime.js',
strftime = require(libFilename),
strftimeUTC = strftime.utc(),
Time = new Date(1307472705067); // Tue, 07 Jun 2011 18:51:45 GMT
assert.fn = function(value, msg) {
assert.equal('function', typeof value, msg);
};
function assertFormat(time, format, expected, name, strftime) {
var actual = strftime(format, time);
assert.equal(expected, actual, name + '("' + format + '", ' + time + ') is ' + JSON.stringify(actual) + ', expected ' + JSON.stringify(expected));
}
assert.format = function(format, expected, expectedUTC, time) {
time = time || Time;
if (expected) { assertFormat(time, format, expected, 'strftime', strftime); }
assertFormat(time, format, expectedUTC || expected, 'strftime.utc()', strftimeUTC);
};
/// check deprecated exports
assert.fn(strftime.strftime);
assert.fn(strftime.strftimeTZ);
assert.fn(strftime.strftimeUTC);
assert.fn(strftime.localizedStrftime);
ok('Deprecated exports');
/// check exports
assert.fn(strftime.localize);
assert.fn(strftime.timezone);
assert.fn(strftime.utc);
ok('Exports');
/// time zones
if (process.env.TZ == 'America/Vancouver') {
testTimezone('P[DS]T');
assert.format('%C', '01', '01', new Date(100, 0, 1));
assert.format('%X', '11:51:45', '18:51:45');
assert.format('%c', 'Tue Jun 07 11:51:45 2011', 'Tue Jun 07 18:51:45 2011');
assert.format('%j', '097', '098', new Date(1365390736236));
assert.format('%x', '06/07/11');
assert.format('%U', '12', null, new Date('2017-03-25 00:00:00 +0000'));
assert.format('%U', '12', '13', new Date('2017-03-26 00:00:00 +0000'));
assert.format('%U', '13', null, new Date('2017-03-27 00:00:00 +0000'));
assert.format('%U', '13', '14', new Date('2017-04-02 00:00:00 +0000'));
ok('Time zones (' + process.env.TZ + ')');
}
else if (process.env.TZ == 'CET') {
testTimezone('CES?T');
assert.format('%C', '01', '00', new Date(100, 0, 1));
assert.format('%X', '20:51:45', '18:51:45');
assert.format('%c', 'Tue Jun 07 20:51:45 2011', 'Tue Jun 07 18:51:45 2011');
assert.format('%j', '098', '098', new Date(1365390736236));
assert.format('%x', '06/07/11');
assert.format('%U', '12', null, new Date('2017-03-25 00:00:00 +0000'));
assert.format('%U', '13', null, new Date('2017-03-26 00:00:00 +0000'));
assert.format('%U', '13', null, new Date('2017-03-27 00:00:00 +0000'));
assert.format('%U', '14', null, new Date('2017-04-02 00:00:00 +0000'));
ok('Time zones (' + process.env.TZ + ')');
}
else {
console.log('(Current timezone has no tests: ' + (process.env.TZ || 'none') + ')');
}
/// check all formats in GMT, most coverage
assert.format('%A', 'Tuesday');
assert.format('%a', 'Tue');
assert.format('%B', 'June');
assert.format('%b', 'Jun');
assert.format('%C', '20');
assert.format('%D', '06/07/11');
assert.format('%d', '07');
assert.format('%-d', '7');
assert.format('%_d', ' 7');
assert.format('%0d', '07');
assert.format('%e', ' 7');
assert.format('%F', '2011-06-07');
assert.format('%H', null, '18');
assert.format('%h', 'Jun');
assert.format('%I', null, '06');
assert.format('%-I', null, '6');
assert.format('%_I', null, ' 6');
assert.format('%0I', null, '06');
assert.format('%j', null, '158');
assert.format('%k', null, '18');
assert.format('%L', '067');
assert.format('%l', null, ' 6');
assert.format('%-l', null, '6');
assert.format('%_l', null, ' 6');
assert.format('%0l', null, '06');
assert.format('%M', null, '51');
assert.format('%m', '06');
assert.format('%n', '\n');
assert.format('%o', '7th');
assert.format('%P', null, 'pm');
assert.format('%p', null, 'PM');
assert.format('%R', null, '18:51');
assert.format('%r', null, '06:51:45 PM');
assert.format('%S', '45');
assert.format('%s', '1307472705');
assert.format('%T', null, '18:51:45');
assert.format('%t', '\t');
assert.format('%U', '23');
assert.format('%U', '24', null, new Date(+Time + 5 * 86400000));
assert.format('%u', '2');
assert.format('%v', ' 7-Jun-2011');
assert.format('%W', '23');
assert.format('%W', '23', null, new Date(+Time + 5 * 86400000));
assert.format('%w', '2');
assert.format('%Y', '2011');
assert.format('%y', '11');
assert.format('%Z', null, 'GMT');
assert.format('%z', null, '+0000');
assert.format('%:z', null, '+00:00');
assert.format('%%', '%'); // any other char
assert.format('%F %T', null, '1970-01-01 00:00:00', new Date(0));
assert.format('%U', null, '12', new Date('2017-03-25 00:00:00 +0000'));
assert.format('%U', null, '13', new Date('2017-03-26 00:00:00 +0000'));
assert.format('%U', null, '13', new Date('2017-03-27 00:00:00 +0000'));
assert.format('%U', null, '14', new Date('2017-04-02 00:00:00 +0000'));
ok('GMT');
/// locales
var it_IT = {
days: words('domenica lunedi martedi mercoledi giovedi venerdi sabato'),
shortDays: words('dom lun mar mer gio ven sab'),
months: words('gennaio febbraio marzo aprile maggio giugno luglio agosto settembre ottobre novembre dicembre'),
shortMonths: words('gen feb mar apr mag giu lug ago set ott nov dic'),
AM: 'it$AM',
PM: 'it$PM',
am: 'it$am',
pm: 'it$pm',
formats: {
D: 'it$%m/%d/%y',
F: 'it$%Y-%m-%d',
R: 'it$%H:%M',
T: 'it$%H:%M:%S',
X: '%T',
c: '%a %b %d %X %Y',
r: 'it$%I:%M:%S %p',
v: 'it$%e-%b-%Y',
x: '%D'
}
};
var strftimeIT = strftime.localize(it_IT),
strftimeITUTC = strftimeIT.utc();
assert.format_it = function(format, expected, expectedUTC) {
if (expected) { assertFormat(Time, format, expected, 'strftime.localize(it_IT)', strftimeIT); }
assertFormat(Time, format, expectedUTC || expected, 'strftime.localize(it_IT).utc()', strftimeITUTC);
};
assert.format_it('%A', 'martedi');
assert.format_it('%a', 'mar');
assert.format_it('%B', 'giugno');
assert.format_it('%b', 'giu');
assert.format_it('%c', null, 'mar giu 07 it$18:51:45 2011');
assert.format_it('%D', 'it$06/07/11');
assert.format_it('%F', 'it$2011-06-07');
assert.format_it('%p', null, 'it$PM');
assert.format_it('%P', null, 'it$pm');
assert.format_it('%R', null, 'it$18:51');
assert.format_it('%r', null, 'it$06:51:45 it$PM');
assert.format_it('%T', null, 'it$18:51:45');
assert.format_it('%v', 'it$ 7-giu-2011');
assert.format_it('%x', null, 'it$06/07/11');
assert.format_it('%X', null, 'it$18:51:45');
ok('Localization');
/// timezones
assert.formatTZ = function(format, expected, tz, time) {
assertFormat(time || Time, format, expected, 'strftime.timezone(' + tz + ')', strftime.timezone(tz));
};
assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', 0);
assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', '+0000');
assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', 120);
assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', '+0200');
assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', -420);
assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', '-0700');
assert.formatTZ('%F %r %z', '2011-06-07 11:21:45 AM -0730', '-0730');
assert.formatTZ('%F %r %:z', '2011-06-07 11:21:45 AM -07:30', '-0730');
ok('Time zone offset');
/// caching
(function() {
// this test fails when the 2 calls cross a millisecond boundary, so try a number of times
var CacheAttempts = 10;
var MaxFailures = 1;
var failures = 0;
for (var i = 0; i < CacheAttempts; ++i) {
var expectedMillis = strftime('%L');
var millis = strftime('%L');
if (expectedMillis != millis) {
++failures;
}
}
if (failures > MaxFailures) {
assert.fail('timestamp caching appears to be broken (' + failures + ' failed attempts out of ' + CacheAttempts + ')');
}
}());
ok('Cached timestamps');
/// helpers
function words(s) { return (s || '').split(' '); }
function ok(s) { console.log('[ \033[32mOK\033[0m ] ' + s); }
// Pass a regex or string that matches the timezone abbrev, e.g. %Z above.
// Don't pass GMT! Every date includes it and it will fail.
// Be careful if you pass a regex, it has to quack like the default one.
function testTimezone(regex) {
regex = typeof regex === 'string' ? RegExp('\\((' + regex + ')\\)$') : regex;
var match = Time.toString().match(regex);
if (match) {
var off = Time.getTimezoneOffset(),
hourOff = off / 60,
hourDiff = Math.floor(hourOff),
hours = 18 - hourDiff,
padSpace24 = hours < 10 ? ' ' : '',
padZero24 = hours < 10 ? '0' : '',
hour24 = String(hours),
padSpace12 = (hours % 12) < 10 ? ' ' : '',
padZero12 = (hours % 12) < 10 ? '0' : '',
hour12 = String(hours % 12),
sign = hourDiff < 0 ? '+' : '-',
minDiff = Time.getTimezoneOffset() - (hourDiff * 60),
mins = String(51 - minDiff),
tz = match[1],
ampm = hour12 == hour24 ? 'AM' : 'PM',
R = hour24 + ':' + mins,
r = padZero12 + hour12 + ':' + mins + ':45 ' + ampm,
T = R + ':45';
assert.format('%H', padZero24 + hour24, '18');
assert.format('%I', padZero12 + hour12, '06');
assert.format('%k', padSpace24 + hour24, '18');
assert.format('%l', padSpace12 + hour12, ' 6');
assert.format('%M', mins);
assert.format('%P', ampm.toLowerCase(), 'pm');
assert.format('%p', ampm, 'PM');
assert.format('%R', R, '18:51');
assert.format('%r', r, '06:51:45 PM');
assert.format('%T', T, '18:51:45');
assert.format('%Z', tz, 'GMT');
assert.format('%z', sign + '0' + Math.abs(hourDiff) + '00', '+0000');
assert.format('%:z', sign + '0' + Math.abs(hourDiff) + ':00', '+00:00');
}
}

View file

@ -1,224 +0,0 @@
#!/usr/bin/env node
// Based on CoffeeScript by andrewschaaf on github
//
// TODO:
// - past and future dates, especially < 1900 and > 2100
// - look for edge cases
var assert = require('assert')
, libFilename = process.argv[2] || '../strftime.js'
, lib = require(libFilename)
// Tue, 07 Jun 2011 18:51:45 GMT
, TestTime = new Date(1307472705067)
assert.fn = function(value, msg) {
assert.equal('function', typeof value, msg)
}
assert.format = function(format, expected, expectedUTC, time) {
time = time || TestTime
function _assertFmt(expected, name) {
name = name || 'strftime'
var actual = lib[name](format, time)
assert.equal(expected, actual,
name + '("' + format + '", ' + time + ') is ' + JSON.stringify(actual)
+ ', expected ' + JSON.stringify(expected))
}
if (expected) _assertFmt(expected, 'strftime')
_assertFmt(expectedUTC || expected, 'strftimeUTC')
}
/// check exports
assert.fn(lib.strftime)
assert.fn(lib.strftimeUTC)
assert.fn(lib.localizedStrftime)
ok('Exports')
/// time zones
if (process.env.TZ == 'America/Vancouver') {
testTimezone('P[DS]T')
assert.format('%C', '01', '01', new Date(100, 0, 1))
assert.format('%j', '097', '098', new Date(1365390736236))
ok('Time zones (' + process.env.TZ + ')')
}
else if (process.env.TZ == 'CET') {
testTimezone('CES?T')
assert.format('%C', '01', '00', new Date(100, 0, 1))
assert.format('%j', '098', '098', new Date(1365390736236))
ok('Time zones (' + process.env.TZ + ')')
}
else {
console.log('(Current timezone has no tests: ' + (process.env.TZ || 'none') + ')')
}
/// check all formats in GMT, most coverage
assert.format('%A', 'Tuesday')
assert.format('%a', 'Tue')
assert.format('%B', 'June')
assert.format('%b', 'Jun')
assert.format('%C', '20')
assert.format('%D', '06/07/11')
assert.format('%d', '07')
assert.format('%-d', '7')
assert.format('%_d', ' 7')
assert.format('%0d', '07')
assert.format('%e', ' 7')
assert.format('%F', '2011-06-07')
assert.format('%H', null, '18')
assert.format('%h', 'Jun')
assert.format('%I', null, '06')
assert.format('%-I', null, '6')
assert.format('%_I', null, ' 6')
assert.format('%0I', null, '06')
assert.format('%j', null, '158')
assert.format('%k', null, '18')
assert.format('%L', '067')
assert.format('%l', null, ' 6')
assert.format('%-l', null, '6')
assert.format('%_l', null, ' 6')
assert.format('%0l', null, '06')
assert.format('%M', null, '51')
assert.format('%m', '06')
assert.format('%n', '\n')
assert.format('%o', '7th')
assert.format('%P', null, 'pm')
assert.format('%p', null, 'PM')
assert.format('%R', null, '18:51')
assert.format('%r', null, '06:51:45 PM')
assert.format('%S', '45')
assert.format('%s', '1307472705')
assert.format('%T', null, '18:51:45')
assert.format('%t', '\t')
assert.format('%U', '23')
assert.format('%U', '24', null, new Date(+TestTime + 5 * 86400000))
assert.format('%u', '2')
assert.format('%v', ' 7-Jun-2011')
assert.format('%W', '23')
assert.format('%W', '23', null, new Date(+TestTime + 5 * 86400000))
assert.format('%w', '2')
assert.format('%Y', '2011')
assert.format('%y', '11')
assert.format('%Z', null, 'GMT')
assert.format('%z', null, '+0000')
assert.format('%%', '%') // any other char
ok('GMT')
/// locales
var it_IT =
{ days: words('domenica lunedi martedi mercoledi giovedi venerdi sabato')
, shortDays: words('dom lun mar mer gio ven sab')
, months: words('gennaio febbraio marzo aprile maggio giugno luglio agosto settembre ottobre novembre dicembre')
, shortMonths: words('gen feb mar apr mag giu lug ago set ott nov dic')
, AM: 'it$AM'
, PM: 'it$PM'
, am: 'it$am'
, pm: 'it$pm'
, formats: {
D: 'it$%m/%d/%y'
, F: 'it$%Y-%m-%d'
, R: 'it$%H:%M'
, r: 'it$%I:%M:%S %p'
, T: 'it$%H:%M:%S'
, v: 'it$%e-%b-%Y'
}
}
assert.format_it = function(format, expected, expectedUTC) {
function _assertFmt(expected, name) {
name = name || 'strftime'
var actual = lib[name](format, TestTime, it_IT)
assert.equal(expected, actual,
name + '("' + format + '", Time) is ' + JSON.stringify(actual)
+ ', expected ' + JSON.stringify(expected))
}
if (expected) _assertFmt(expected, 'strftime')
_assertFmt(expectedUTC || expected, 'strftimeUTC')
}
assert.format_it('%A', 'martedi')
assert.format_it('%a', 'mar')
assert.format_it('%B', 'giugno')
assert.format_it('%b', 'giu')
assert.format_it('%D', 'it$06/07/11')
assert.format_it('%F', 'it$2011-06-07')
assert.format_it('%p', null, 'it$PM')
assert.format_it('%P', null, 'it$pm')
assert.format_it('%R', null, 'it$18:51')
assert.format_it('%r', null, 'it$06:51:45 it$PM')
assert.format_it('%T', null, 'it$18:51:45')
assert.format_it('%v', 'it$ 7-giu-2011')
ok('Localization')
/// timezones
assert.formatTZ = function(format, expected, tz, time) {
time = time || TestTime;
var actual = lib.strftimeTZ(format, time, tz)
assert.equal(
expected, actual,
('strftime("' + format + '", ' + time + ') is ' + JSON.stringify(actual) + ', expected ' + JSON.stringify(expected))
)
}
assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', 0)
assert.formatTZ('%F %r %z', '2011-06-07 06:51:45 PM +0000', '+0000')
assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', 120)
assert.formatTZ('%F %r %z', '2011-06-07 08:51:45 PM +0200', '+0200')
assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', -420)
assert.formatTZ('%F %r %z', '2011-06-07 11:51:45 AM -0700', '-0700')
assert.formatTZ('%F %r %z', '2011-06-07 11:21:45 AM -0730', '-0730')
ok('Time zone offset')
/// helpers
function words(s) { return (s || '').split(' '); }
function ok(s) { console.log('[ \033[32mOK\033[0m ] ' + s) }
// Pass a regex or string that matches the timezone abbrev, e.g. %Z above.
// Don't pass GMT! Every date includes it and it will fail.
// Be careful if you pass a regex, it has to quack like the default one.
function testTimezone(regex) {
regex = typeof regex === 'string' ? RegExp('\\((' + regex + ')\\)$') : regex
var match = TestTime.toString().match(regex)
if (match) {
var off = TestTime.getTimezoneOffset()
, hourOff = off / 60
, hourDiff = Math.floor(hourOff)
, hours = 18 - hourDiff
, padSpace24 = hours < 10 ? ' ' : ''
, padZero24 = hours < 10 ? '0' : ''
, hour24 = String(hours)
, padSpace12 = (hours % 12) < 10 ? ' ' : ''
, padZero12 = (hours % 12) < 10 ? '0' : ''
, hour12 = String(hours % 12)
, sign = hourDiff < 0 ? '+' : '-'
, minDiff = TestTime.getTimezoneOffset() - (hourDiff * 60)
, mins = String(51 - minDiff)
, tz = match[1]
, ampm = hour12 == hour24 ? 'AM' : 'PM'
, R = hour24 + ':' + mins
, r = padZero12 + hour12 + ':' + mins + ':45 ' + ampm
, T = R + ':45'
assert.format('%H', padZero24 + hour24, '18')
assert.format('%I', padZero12 + hour12, '06')
assert.format('%k', padSpace24 + hour24, '18')
assert.format('%l', padSpace12 + hour12, ' 6')
assert.format('%M', mins)
assert.format('%P', ampm.toLowerCase(), 'pm')
assert.format('%p', ampm, 'PM')
assert.format('%R', R, '18:51')
assert.format('%r', r, '06:51:45 PM')
assert.format('%T', T, '18:51:45')
assert.format('%Z', tz, 'GMT')
assert.format('%z', sign + '0' + Math.abs(hourDiff) + '00', '+0000')
}
}