OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * General purpose date/time utilities. | |
7 */ | |
8 class DateUtils { | |
9 // TODO(jmesserly): localized strings | |
10 static final WEEKDAYS = const ['Monday', 'Tuesday', 'Wednesday', 'Thursday', | |
11 'Friday', 'Saturday', 'Sunday']; | |
12 | |
13 static final YESTERDAY = 'Yesterday'; | |
14 | |
15 static final MS_IN_WEEK = Date.DAYS_IN_WEEK * Duration.MILLISECONDS_PER_DAY; | |
16 | |
17 // TODO(jmesserly): workaround for missing Date.fromDate in Dartium | |
18 // Remove this once that is implemented. See b/5055106 | |
19 // Parse a string like: "Mon, 27 Jun 2011 15:22:00 -0700" | |
20 static Date fromString(String text) { | |
21 final parts = text.split(' '); | |
22 if (parts.length == 1) { | |
23 return _parseIsoDate(text); | |
24 } | |
25 | |
26 if (parts.length != 6) { | |
27 throw 'bad date format, expected 6 parts: ' + text; | |
28 } | |
29 | |
30 // skip parts[0], the weekday | |
31 | |
32 int day = Math.parseInt(parts[1]); | |
33 | |
34 final months = const['Jan', 'Feb', 'Mar', 'Apr', | |
35 'May', 'Jun', 'Jul', 'Aug', | |
36 'Sep', 'Oct', 'Nov', 'Dec']; | |
37 int month = months.indexOf(parts[2], 0) + 1; | |
38 if (month < 0) { | |
39 throw 'bad month, expected 3 letter month code, got: ' + parts[2]; | |
40 } | |
41 | |
42 int year = Math.parseInt(parts[3]); | |
43 | |
44 final timeParts = parts[4].split(':'); | |
45 if (timeParts.length != 3) { | |
46 throw 'bad time format, expected 3 parts: ' + parts[4]; | |
47 } | |
48 | |
49 int hours = Math.parseInt(timeParts[0]); | |
50 int minutes = Math.parseInt(timeParts[1]); | |
51 int seconds = Math.parseInt(timeParts[2]); | |
52 | |
53 // TODO(jmesserly): TimeZone is not implemented in Dartium. This ugly | |
54 // hack applies the timezone from the string to the final time | |
55 int zoneOffset = Math.parseInt(parts[5]) ~/ 100; | |
56 | |
57 // Pretend it's a UTC time | |
58 Date result = new Date.withTimeZone( | |
59 year, month, day, hours, minutes, seconds, 0, new TimeZone.utc()); | |
60 // Shift it to the proper zone, but it's still a UTC time | |
61 result = result.subtract(new Duration(hours: zoneOffset)); | |
62 // Then render it as a local time | |
63 return result.changeTimeZone(new TimeZone.local()); | |
64 } | |
65 | |
66 /** Parse a string like: 2011-07-19T22:03:04.000Z */ | |
67 // TODO(jmesserly): workaround for Date.fromDate, which has issues: | |
68 // * on Dart VM it doesn't handle all of ISO 8601. See b/5055106. | |
69 // * on DartC it doesn't work on Safari. See b/5062557. | |
70 // Remove this once that function is fully implemented | |
71 static Date _parseIsoDate(String text) { | |
72 void ensure(bool value) { | |
73 if (!value) { | |
74 throw 'bad date format, expected YYYY-MM-DDTHH:MM:SS.mmmZ: ' + text; | |
75 } | |
76 } | |
77 | |
78 TimeZone zone; | |
79 if (text.endsWith('Z')) { | |
80 text = text.substring(0, text.length - 1); | |
81 zone = new TimeZone.utc(); | |
82 } else { | |
83 zone = new TimeZone.local(); | |
84 } | |
85 | |
86 final parts = text.split('T'); | |
87 ensure(parts.length == 2); | |
88 | |
89 final date = parts[0].split('-'); | |
90 ensure(date.length == 3); | |
91 | |
92 final time = parts[1].split(':'); | |
93 ensure(time.length == 3); | |
94 | |
95 final seconds = time[2].split('.'); | |
96 ensure(seconds.length >= 1 && seconds.length <= 2); | |
97 int milliseconds = 0; | |
98 if (seconds.length == 2) { | |
99 milliseconds = Math.parseInt(seconds[1]); | |
100 } | |
101 | |
102 return new Date.withTimeZone( | |
103 Math.parseInt(date[0]), | |
104 Math.parseInt(date[1]), | |
105 Math.parseInt(date[2]), | |
106 Math.parseInt(time[0]), | |
107 Math.parseInt(time[1]), | |
108 Math.parseInt(seconds[0]), | |
109 milliseconds, | |
110 zone); | |
111 } | |
112 | |
113 /** | |
114 * A date/time formatter that takes into account the current date/time: | |
115 * - if it's from today, just show the time | |
116 * - if it's from yesterday, just show 'Yesterday' | |
117 * - if it's from the same week, just show the weekday | |
118 * - otherwise, show just the date | |
119 */ | |
120 static String toRecentTimeString(Date then) { | |
121 bool datesAreEqual(Date d1, Date d2) { | |
122 return (d1.year == d2.year) && (d1.month == d2.month) && | |
123 (d1.day == d2.day); | |
124 } | |
125 | |
126 final now = new Date.now(); | |
127 if (datesAreEqual(then, now)) { | |
128 return toHourMinutesString(new Duration( | |
129 0, then.hours, then.minutes, then.seconds, then.milliseconds)); | |
130 } | |
131 | |
132 final today = new Date(now.year, now.month, now.day, 0, 0, 0, 0); | |
133 Duration delta = today.difference(then); | |
134 if (delta.inMilliseconds < Duration.MILLISECONDS_PER_DAY) { | |
135 return YESTERDAY; | |
136 } else if (delta.inMilliseconds < MS_IN_WEEK) { | |
137 return WEEKDAYS[getWeekday(then)]; | |
138 } else { | |
139 // TODO(jmesserly): locale specific date format | |
140 String twoDigits(int n) { | |
141 if (n >= 10) return "${n}"; | |
142 return "0${n}"; | |
143 } | |
144 String twoDigitMonth = twoDigits(then.month); | |
145 String twoDigitDay = twoDigits(then.day); | |
146 return "${then.year}-${twoDigitMonth}-${twoDigitDay}"; | |
147 } | |
148 } | |
149 | |
150 // TODO(jmesserly): this is a workaround for unimplemented Date.weekday | |
151 // Code inspired by v8/src/date.js | |
152 static int getWeekday(Date dateTime) { | |
153 final unixTimeStart = new Date(1970, 1, 1, 0, 0, 0, 0); | |
154 int msSince1970 = dateTime.difference(unixTimeStart).inMilliseconds; | |
155 int daysSince1970 = msSince1970 ~/ Duration.MILLISECONDS_PER_DAY; | |
156 // 1970-1-1 was Thursday | |
157 return ((daysSince1970 + Date.THU) % Date.DAYS_IN_WEEK); | |
158 } | |
159 | |
160 /** Formats a time in H:MM A format */ | |
161 // TODO(jmesserly): should get 12 vs 24 hour clock setting from the locale | |
162 static String toHourMinutesString(Duration duration) { | |
163 assert(duration.inDays == 0); | |
164 int hours = duration.inHours; | |
165 String a; | |
166 if (hours >= 12) { | |
167 a = 'pm'; | |
168 if (hours != 12) { | |
169 hours -= 12; | |
170 } | |
171 } else { | |
172 a = 'am'; | |
173 if (hours == 0) { | |
174 hours += 12; | |
175 } | |
176 } | |
177 String twoDigits(int n) { | |
178 if (n >= 10) return "${n}"; | |
179 return "0${n}"; | |
180 } | |
181 String mm = | |
182 twoDigits(duration.inMinutes.remainder(Duration.MINUTES_PER_HOUR)); | |
183 return "${hours}:${mm} ${a}"; | |
184 } | |
185 } | |
OLD | NEW |