This repository has been archived by the owner on Nov 3, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathl10n_date.js
223 lines (192 loc) · 6.46 KB
/
l10n_date.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
/**
* This lib relies on `l10n.js' to implement localizable date/time strings.
*
* The proposed `DateTimeFormat' object should provide all the features that are
* planned for the `Intl.DateTimeFormat' constructor, but the API does not match
* exactly the ES-i18n draft.
* - https://bugzilla.mozilla.org/show_bug.cgi?id=769872
* - http://wiki.ecmascript.org/doku.php?id=globalization:specification_drafts
*
* Besides, this `DateTimeFormat' object provides two features that aren't
* planned in the ES-i18n spec:
* - a `toLocaleFormat()' that really works (i.e. fully translated);
* - a `fromNow()' method to handle relative dates ("pretty dates").
*
* WARNING: this library relies on the non-standard `toLocaleFormat()' method,
* which is specific to Firefox -- no other browser is supported.
*/
navigator.mozL10n.DateTimeFormat = function(locales, options) {
var _ = navigator.mozL10n.get;
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date/toLocaleFormat
//
// Deprecated. Please, use Intl API instead
function localeFormat(d, format) {
var tokens = format.match(/(%E.|%O.|%.)/g);
for (var i = 0; tokens && i < tokens.length; i++) {
var value = '';
// http://pubs.opengroup.org/onlinepubs/007908799/xsh/strftime.html
switch (tokens[i]) {
// localized day/month names
case '%a':
value = _('weekday-' + d.getDay() + '-short');
break;
case '%A':
value = _('weekday-' + d.getDay() + '-long');
break;
case '%b':
case '%h':
value = _('month-' + d.getMonth() + '-short');
break;
case '%B':
value = _('month-' + d.getMonth() + '-long');
break;
case '%Eb':
value = _('month-' + d.getMonth() + '-genitive');
break;
// like %H, but in 12-hour format and without any leading zero
case '%I':
value = d.getHours() % 12 || 12;
break;
// like %d, without any leading zero
case '%e':
value = d.getDate();
break;
// %p: 12 hours format (AM/PM)
case '%p':
value = d.getHours() < 12 ? _('time_am') : _('time_pm');
break;
// localized date/time strings
case '%c':
case '%x':
case '%X':
// ensure the localized format string doesn't contain any %c|%x|%X
var tmp = _('dateTimeFormat_' + tokens[i]);
if (tmp && !(/(%c|%x|%X)/).test(tmp)) {
value = localeFormat(d, tmp);
}
break;
// other tokens don't require any localization
}
format = format.replace(tokens[i], value || d.toLocaleFormat(tokens[i]));
}
return format;
}
/**
* Returns the parts of a number of seconds
*/
function relativeParts(seconds) {
seconds = Math.abs(seconds);
var descriptors = {};
var units = [
'years', 86400 * 365,
'months', 86400 * 30,
'weeks', 86400 * 7,
'days', 86400,
'hours', 3600,
'minutes', 60
];
if (seconds < 60) {
return {
minutes: Math.round(seconds / 60)
};
}
for (var i = 0, uLen = units.length; i < uLen; i += 2) {
var value = units[i + 1];
if (seconds >= value) {
descriptors[units[i]] = Math.floor(seconds / value);
seconds -= descriptors[units[i]] * value;
}
}
return descriptors;
}
/**
* Returns a translated string which respresents the
* relative time before or after a date.
*
* Deprecated: Please, use relativeDate for now
*
* @param {String|Date} time before/after the currentDate.
* @param {String} useCompactFormat whether to use a compact display format.
* @param {Number} maxDiff returns a formatted date if the diff is greater.
*/
function prettyDate(time, useCompactFormat, maxDiff) {
maxDiff = maxDiff || 86400 * 10; // default = 10 days
switch (time.constructor) {
case String: // timestamp
time = parseInt(time);
break;
case Date:
time = time.getTime();
break;
}
var secDiff = (Date.now() - time) / 1000;
if (isNaN(secDiff)) {
return _('incorrectDate');
}
if (Math.abs(secDiff) > 60) {
// round milliseconds up if difference is over 1 minute so the result is
// closer to what the user would expect (1h59m59s300ms diff should return
// "in 2 hours" instead of "in an hour")
secDiff = secDiff > 0 ? Math.ceil(secDiff) : Math.floor(secDiff);
}
if (secDiff > maxDiff) {
return localeFormat(new Date(time), '%x');
}
var f = useCompactFormat ? '-short' : '-long';
var parts = relativeParts(secDiff);
var affix = secDiff >= 0 ? '-ago' : '-until';
for (var i in parts) {
return _(i + affix + f, { value: parts[i]});
}
}
/**
* Async clone of prettyDate.
* Temporary solution while we're waiting for Intl extension to support
* relative dates.
*/
function relativeDate(time, useCompactFormat, maxDiff) {
maxDiff = maxDiff || 86400 * 10; // default = 10 days
switch (time.constructor) {
case String: // timestamp
time = parseInt(time);
break;
case Date:
time = time.getTime();
break;
}
var secDiff = (Date.now() - time) / 1000;
if (isNaN(secDiff)) {
return navigator.mozL10n.formatValue('incorrectDate');
}
if (Math.abs(secDiff) > 60) {
// round milliseconds up if difference is over 1 minute so the result is
// closer to what the user would expect (1h59m59s300ms diff should return
// "in 2 hours" instead of "in an hour")
secDiff = secDiff > 0 ? Math.ceil(secDiff) : Math.floor(secDiff);
}
if (secDiff > maxDiff) {
var dateString = new Date(time).toLocaleString(navigator.languages, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
});
return Promise.resolve(dateString);
}
var f = useCompactFormat ? '-short' : '-long';
var parts = relativeParts(secDiff);
var affix = secDiff >= 0 ? '-ago' : '-until';
for (var i in parts) {
return navigator.mozL10n.formatValue(i + affix + f, { value: parts[i]});
}
}
// API
return {
localeFormat: localeFormat,
fromNow: prettyDate,
relativeDate: relativeDate,
relativeParts: relativeParts
};
};