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