Merge lp:~jjacobs/methanal/utc-times into lp:methanal

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
Reviewer Review Type Date Requested Status
Tristan Seligmann Approve
Review via email: mp+13124@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Tristan Seligmann (mithrandi) wrote :

> 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?

review: Needs Information
Revision history for this message
Jonathan Jacobs (jjacobs) wrote :

> > 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?

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 }

Subscribers

People subscribed via source and target branches