Merge lp:~jjacobs/methanal/utc-times into lp:methanal
- utc-times
- Merge into trunk
Proposed by
Jonathan Jacobs
Status: | Merged |
---|---|
Approved by: | Tristan Seligmann |
Approved revision: | 107 |
Merged at revision: | not available |
Proposed branch: | lp:~jjacobs/methanal/utc-times |
Merge into: | lp:methanal |
Diff against target: |
370 lines 4 files modified
methanal/js/Methanal/Tests/TestUtil.js (+34/-18) methanal/js/Methanal/Tests/TestView.js (+3/-3) methanal/js/Methanal/Util.js (+72/-42) methanal/js/Methanal/View.js (+7/-1) |
To merge this branch: | bzr merge lp:~jjacobs/methanal/utc-times |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tristan Seligmann | Approve | ||
Review via email: mp+13124@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Jonathan Jacobs (jjacobs) wrote : | # |
> > 7 self._knownTime = Methanal.
> > 8 - new Date(2009, 8, 6, 1, 36, 23, 2));
> > 9 + new Date(2009, 8, 6, 1, 36, 23, 2), true);
>
> As far as I can tell, fromDate only takes one parameter; what is the point of
> passing true here?
Something left in from an older API. I've removed it and pushed a new revision.
Revision history for this message
Tristan Seligmann (mithrandi) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'methanal/js/Methanal/Tests/TestUtil.js' |
2 | --- methanal/js/Methanal/Tests/TestUtil.js 2009-10-02 01:16:09 +0000 |
3 | +++ methanal/js/Methanal/Tests/TestUtil.js 2009-10-09 22:36:14 +0000 |
4 | @@ -220,17 +220,20 @@ |
5 | function assertTimeParsed(data, timestamp) { |
6 | var time = Methanal.Util.Time.guess(data); |
7 | self.assertIdentical(time._oneDay, true); |
8 | - self.assertIdentical(time.asTimestamp(), timestamp); |
9 | + // User input is interpreted as local time, but the tests should |
10 | + // pass regardless of the runner's local timezone, so we use UTC |
11 | + // dates. |
12 | + self.assertIdentical(time.asUTCDate().getTime(), timestamp); |
13 | }; |
14 | |
15 | - assertTimeParsed('2009/9/1', 1251756000000); |
16 | - assertTimeParsed('2009.09.01', 1251756000000); |
17 | - assertTimeParsed('2009-09-01', 1251756000000); |
18 | - assertTimeParsed('1/9/2009', 1251756000000); |
19 | - assertTimeParsed('01.09.2009', 1251756000000); |
20 | - assertTimeParsed('01-09-2009', 1251756000000); |
21 | - assertTimeParsed('1/9/2009', 1251756000000); |
22 | - assertTimeParsed('29/2/2008', 1204236000000); |
23 | + assertTimeParsed('2009/9/1', 1251763200000); |
24 | + assertTimeParsed('2009.09.01', 1251763200000); |
25 | + assertTimeParsed('2009-09-01', 1251763200000); |
26 | + assertTimeParsed('1/9/2009', 1251763200000); |
27 | + assertTimeParsed('01.09.2009', 1251763200000); |
28 | + assertTimeParsed('01-09-2009', 1251763200000); |
29 | + assertTimeParsed('1/9/2009', 1251763200000); |
30 | + assertTimeParsed('29/2/2008', 1204243200000); |
31 | }, |
32 | |
33 | |
34 | @@ -264,9 +267,9 @@ |
35 | * Create a L{Methanal.Util.Time} instance from a C{Date}. |
36 | */ |
37 | function test_fromDate(self) { |
38 | - var d = new Date(); |
39 | + var d = new Date(2009, 8, 1, 12, 34, 56, 78); |
40 | var t = Methanal.Util.Time.fromDate(d); |
41 | - self.assertIdentical(t.asTimestamp(), d.getTime()); |
42 | + self.assertIdentical(t.asDate().getTime(), d.getTime()); |
43 | }, |
44 | |
45 | |
46 | @@ -309,19 +312,32 @@ |
47 | * Create a L{Methanal.Util.Time} instance from a timestamp in milliseconds. |
48 | */ |
49 | function test_fromTimestamp(self) { |
50 | - var t = Methanal.Util.Time.fromTimestamp(1251759723000); |
51 | - self.assertIdentical(t.asHumanly(), 'Tue, 1 Sep 2009 01:02:03 am'); |
52 | + var t; |
53 | + var timestamp; |
54 | + |
55 | + timestamp = 1251766923000; |
56 | + t = Methanal.Util.Time.fromTimestamp(timestamp); |
57 | + self.assertIdentical(t.asUTCDate().getTime(), timestamp); |
58 | + self.assertIdentical(t._timezoneOffset, 0); |
59 | + |
60 | + var d = new Date(); |
61 | + timestamp = d.getTime(); |
62 | + t = Methanal.Util.Time.fromTimestamp(timestamp, d.getTimezoneOffset()); |
63 | + self.assertIdentical(t.asUTCDate().getTime(), timestamp); |
64 | + self.assertIdentical( |
65 | + t._timezoneOffset, d.getTimezoneOffset() * 60 * 1000); |
66 | }, |
67 | |
68 | |
69 | /** |
70 | * L{Methanal.Util.Time.asDate} converts a Time into a C{Date} representing |
71 | - * the same time. |
72 | + * the same I{local} time. |
73 | */ |
74 | function test_asDate(self) { |
75 | var t = Methanal.Util.Time(); |
76 | var d = t.asDate(); |
77 | - self.assertIdentical(t.asTimestamp(), d.getTime()); |
78 | + self.assertIdentical( |
79 | + t.asTimestamp() + d.getTimezoneOffset() * 60 * 1000, d.getTime()); |
80 | }, |
81 | |
82 | |
83 | @@ -330,7 +346,7 @@ |
84 | * milliseconds elapsed since the epoch. |
85 | */ |
86 | function test_asTimestamp(self) { |
87 | - self.assertIdentical(self._knownTime.asTimestamp(), 1252193783002); |
88 | + self.assertIdentical(self._knownTime.asTimestamp(), 1252200983002); |
89 | }, |
90 | |
91 | |
92 | @@ -392,11 +408,11 @@ |
93 | */ |
94 | function test_offset(self) { |
95 | var t = self._knownTime.offset(Methanal.Util.TimeDelta({'days': -1})); |
96 | - self.assertIdentical(t.asTimestamp(), 1252107383002); |
97 | + self.assertIdentical(t.asTimestamp(), 1252114583002); |
98 | self.assertIdentical(t.oneDay().asHumanly(), 'Sat, 5 Sep 2009'); |
99 | |
100 | var t = self._knownTime.offset(Methanal.Util.TimeDelta({'days': 1})); |
101 | - self.assertIdentical(t.asTimestamp(), 1252280183002); |
102 | + self.assertIdentical(t.asTimestamp(), 1252287383002); |
103 | self.assertIdentical(t.oneDay().asHumanly(), 'Mon, 7 Sep 2009'); |
104 | }); |
105 | |
106 | |
107 | === modified file 'methanal/js/Methanal/Tests/TestView.js' |
108 | --- methanal/js/Methanal/Tests/TestView.js 2009-10-04 11:18:09 +0000 |
109 | +++ methanal/js/Methanal/Tests/TestView.js 2009-10-09 22:36:14 +0000 |
110 | @@ -525,8 +525,8 @@ |
111 | self.assertIdentical(called, 0); |
112 | control.setValue('2009-01-01'); |
113 | self.assertIdentical(called, 1); |
114 | - self.assertIdentical(displayValue, |
115 | - Methanal.Util.Time.guess('2009-01-01').asHumanly()); |
116 | + var t = Methanal.Util.Time.fromDate(new Date(2009, 0, 1)); |
117 | + self.assertIdentical(displayValue, t.oneDay().asHumanly()); |
118 | control.onKeyUp(control.inputNode); |
119 | self.assertIdentical(called, 2); |
120 | }); |
121 | @@ -548,7 +548,7 @@ |
122 | control.setValue('NOTAVALIDDATE'); |
123 | self.assertIdentical(control.getValue(), undefined); |
124 | control.setValue('2009-01-01'); |
125 | - self.assertIdentical(control.getValue(), 1230760800000); |
126 | + self.assertIdentical(control.getValue(), 1230768000000); |
127 | }); |
128 | }, |
129 | |
130 | |
131 | === modified file 'methanal/js/Methanal/Util.js' |
132 | --- methanal/js/Methanal/Util.js 2009-10-02 01:16:39 +0000 |
133 | +++ methanal/js/Methanal/Util.js 2009-10-09 22:36:14 +0000 |
134 | @@ -479,26 +479,37 @@ |
135 | |
136 | |
137 | /** |
138 | - * A high-level object built on top of C{Date}. |
139 | + * A high-level time and date object. |
140 | * |
141 | - * @type _date: C{Date} |
142 | - * @ivar _date: Underlying Date instance |
143 | + * @type _timestamp: C{Number} |
144 | + * @ivar _timestamp: Number of milliseconds since the epoch: |
145 | + * January 1, 1970, 00:00:00 UTC |
146 | * |
147 | * @type _oneDay: C{boolean} |
148 | * @ivar _oneDay: Is this a truncated Time instance? |
149 | */ |
150 | Divmod.Class.subclass(Methanal.Util, 'Time').methods( |
151 | function __init__(self) { |
152 | - self._date = new Date(); |
153 | + var d = new Date(); |
154 | + self._timezoneOffset = d.getTimezoneOffset() * 60 * 1000; |
155 | + self._timestamp = d.getTime() - self._timezoneOffset; |
156 | self._oneDay = false; |
157 | }, |
158 | |
159 | |
160 | /** |
161 | - * C{Date} representation. |
162 | + * Local time C{Date} representation. |
163 | */ |
164 | function asDate(self) { |
165 | - return self._date; |
166 | + return new Date(self._timestamp + self._timezoneOffset); |
167 | + }, |
168 | + |
169 | + |
170 | + /** |
171 | + * UTC C{Date} representation. |
172 | + */ |
173 | + function asUTCDate(self) { |
174 | + return new Date(self._timestamp); |
175 | }, |
176 | |
177 | |
178 | @@ -506,7 +517,7 @@ |
179 | * The number of milliseconds since the epoch. |
180 | */ |
181 | function asTimestamp(self) { |
182 | - return self._date.getTime(); |
183 | + return self._timestamp; |
184 | }, |
185 | |
186 | |
187 | @@ -514,7 +525,7 @@ |
188 | * A human-readable string representation. |
189 | */ |
190 | function asHumanly(self, twentyFourHours) { |
191 | - var _date = self._date; |
192 | + var _date = self.asDate(); |
193 | var r = []; |
194 | r.push(self.getDayName(true) + ','); |
195 | r.push(_date.getDate().toString()); |
196 | @@ -522,28 +533,24 @@ |
197 | r.push(_date.getFullYear().toString()); |
198 | |
199 | if (!self._oneDay) { |
200 | - function _humanlyTime(dateWithTime, twentyFourHours) { |
201 | - var prefix = ''; |
202 | - var hours = dateWithTime.getHours(); |
203 | - if (!twentyFourHours) { |
204 | - var dm = Methanal.Util.divmod(hours, 12); |
205 | - prefix = dm[0] > 0 ? ' pm' : ' am'; |
206 | - hours = dm[1] == 0 ? 12 : dm[1]; |
207 | - } |
208 | - |
209 | - function pad(v) { |
210 | - return Methanal.Util.rjust(v.toString(), 2, '0'); |
211 | - }; |
212 | - |
213 | - var r = []; |
214 | - r.push(hours); |
215 | - r.push(dateWithTime.getMinutes()); |
216 | - r.push(dateWithTime.getSeconds()); |
217 | - r = Methanal.Util.map(pad, r); |
218 | - return r.join(':') + prefix; |
219 | + var prefix = ''; |
220 | + var hours = _date.getHours(); |
221 | + if (!twentyFourHours) { |
222 | + var dm = Methanal.Util.divmod(hours, 12); |
223 | + prefix = dm[0] > 0 ? ' pm' : ' am'; |
224 | + hours = dm[1] == 0 ? 12 : dm[1]; |
225 | } |
226 | |
227 | - r.push(_humanlyTime(_date, twentyFourHours)); |
228 | + function pad(v) { |
229 | + return Methanal.Util.rjust(v.toString(), 2, '0'); |
230 | + }; |
231 | + |
232 | + var t = []; |
233 | + t.push(hours); |
234 | + t.push(_date.getMinutes()); |
235 | + t.push(_date.getSeconds()); |
236 | + t = Methanal.Util.map(pad, t); |
237 | + r.push(t.join(':') + prefix); |
238 | } |
239 | |
240 | return r.join(' '); |
241 | @@ -559,7 +566,7 @@ |
242 | * @rtype: C{String} |
243 | */ |
244 | function getDayName(self, shortened) { |
245 | - var name = Methanal.Util.Time._dayNames[self._date.getDay()]; |
246 | + var name = Methanal.Util.Time._dayNames[self.asDate().getDay()]; |
247 | return shortened ? name.substr(0, 3) : name; |
248 | }, |
249 | |
250 | @@ -573,7 +580,7 @@ |
251 | * @rtype: C{String} |
252 | */ |
253 | function getMonthName(self, shortened) { |
254 | - var name = Methanal.Util.Time._monthNames[self._date.getMonth()]; |
255 | + var name = Methanal.Util.Time._monthNames[self.asDate().getMonth()]; |
256 | return shortened ? name.substr(0, 3) : name; |
257 | }, |
258 | |
259 | @@ -585,11 +592,12 @@ |
260 | * @return: A new instance representing the truncated date |
261 | */ |
262 | function oneDay(self) { |
263 | - var d = new Date( |
264 | - self._date.getFullYear(), |
265 | - self._date.getMonth(), |
266 | - self._date.getDate()); |
267 | - var t = Methanal.Util.Time.fromDate(d); |
268 | + var _date = self.asDate(); |
269 | + _date.setHours(0); |
270 | + _date.setMinutes(0); |
271 | + _date.setSeconds(0); |
272 | + _date.setMilliseconds(0); |
273 | + var t = Methanal.Util.Time.fromDate(_date); |
274 | t._oneDay = true; |
275 | return t; |
276 | }, |
277 | @@ -606,9 +614,9 @@ |
278 | * @return: A new instance representing the newly offset time |
279 | */ |
280 | function offset(self, delta) { |
281 | - var d = new Date(self.asTimestamp() + delta); |
282 | - var t = Methanal.Util.Time.fromDate(d); |
283 | + var t = Methanal.Util.Time.fromTimestamp(self.asTimestamp() + delta); |
284 | t._oneDay = self._oneDay; |
285 | + t._timezoneOffset = self._timezoneOffset; |
286 | return t; |
287 | }); |
288 | |
289 | @@ -628,17 +636,35 @@ |
290 | */ |
291 | Methanal.Util.Time.fromDate = function fromDate(dateObj) { |
292 | var t = Methanal.Util.Time(); |
293 | - t._date = dateObj; |
294 | + t._timezoneOffset = dateObj.getTimezoneOffset() * 60 * 1000; |
295 | + t._timestamp = dateObj.getTime() - t._timezoneOffset; |
296 | return t; |
297 | }; |
298 | |
299 | |
300 | |
301 | /** |
302 | - * Create a L{Methanal.Util.Time} instance from a timestamp in milliseconds. |
303 | + * Create a L{Methanal.Util.Time} instance from a timestamp in milliseconds, |
304 | + * since January 1, 1970, 00:00:00 UTC. |
305 | + * |
306 | + * @type timestamp: C{Number} |
307 | + * @param timestamp: Number of milliseconds since the epoch |
308 | + * |
309 | + * @type timezoneOffset: C{Number} |
310 | + * @param timezoneOffset: Timezone offset in minutes |
311 | + * |
312 | + * @rtype: L{Methanal.Util.Time} |
313 | */ |
314 | -Methanal.Util.Time.fromTimestamp = function fromTimestamp(timestamp) { |
315 | - return Methanal.Util.Time.fromDate(new Date(timestamp)); |
316 | +Methanal.Util.Time.fromTimestamp = function fromTimestamp(timestamp, timezoneOffset) { |
317 | + var t = Methanal.Util.Time(); |
318 | + t._timestamp = timestamp; |
319 | + if (timezoneOffset) { |
320 | + timezoneOffset *= 60 * 1000; |
321 | + } else { |
322 | + timezoneOffset = 0; |
323 | + } |
324 | + t._timezoneOffset = timezoneOffset; |
325 | + return t; |
326 | }; |
327 | |
328 | |
329 | @@ -715,6 +741,9 @@ |
330 | /** |
331 | * Create a L{Methanal.Util.Time} instance from a semi-structured string. |
332 | * |
333 | + * As this is primarily intended for textual date input by users, L{value} is |
334 | + * interpreted in local time. |
335 | + * |
336 | * @type value: C{String} |
337 | * @param value: Either a numerical YYYYMMDD or DDMMYYY string (separated by |
338 | * C{/}, C{.} or C{-}) or a relative time reference, as supported by |
339 | @@ -759,10 +788,11 @@ |
340 | y = Methanal.Util.strToInt(parts[2]); |
341 | } |
342 | |
343 | - if (_validDate(y, m, d)) |
344 | + if (_validDate(y, m, d)) { |
345 | // TODO: In the future, "guess" should be able to guess times as |
346 | // well as dates. |
347 | return Methanal.Util.Time.fromDate(new Date(y, m, d)).oneDay(); |
348 | + } |
349 | } |
350 | |
351 | throw new Methanal.Util.TimeParseError( |
352 | |
353 | === modified file 'methanal/js/Methanal/View.js' |
354 | --- methanal/js/Methanal/View.js 2009-10-05 00:15:43 +0000 |
355 | +++ methanal/js/Methanal/View.js 2009-10-09 22:36:14 +0000 |
356 | @@ -1605,7 +1605,13 @@ |
357 | function makeDisplayValue(self, value) { |
358 | var msg = ''; |
359 | try { |
360 | - var time = Methanal.Util.Time.fromTimestamp(value).oneDay(); |
361 | + // XXX: There is probably a potential bug here: If "value" (a UTC |
362 | + // timestamp) falls before the switch-over for daylight savings |
363 | + // before the timezone offset has been corrected for, the timezone |
364 | + // offset given here will be the wrong one. |
365 | + var d = new Date(value); |
366 | + var time = Methanal.Util.Time.fromTimestamp( |
367 | + value, d.getTimezoneOffset()).oneDay(); |
368 | if (time) { |
369 | msg = time.asHumanly(self.twentyFourHours); |
370 | } |
> 7 self._knownTime = Methanal. Util.Time. fromDate(
> 8 - new Date(2009, 8, 6, 1, 36, 23, 2));
> 9 + new Date(2009, 8, 6, 1, 36, 23, 2), true);
As far as I can tell, fromDate only takes one parameter; what is the point of passing true here?