 |
Changeset 3033
- Timestamp:
- 12/16/07 20:46:10
(1 year ago)
- Author:
- kris
- Message:
* added simple DateTime? struct
* Date module usage replaced with Date and DateTime? structs
* Date module removed
* Clocks now use DateTime?
* Calendar.Date replaced with generic Date
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r2978 |
r3033 |
|
| 19 | 19 | { |
|---|
| 20 | 20 | // retreive local time |
|---|
| 21 | | auto date = WallClock.toDate; |
|---|
| | 21 | auto dt = WallClock.toDate; |
|---|
| 22 | 22 | |
|---|
| 23 | 23 | // get GMT difference in minutes |
|---|
| … | … | |
| 29 | 29 | // format date |
|---|
| 30 | 30 | Stdout.formatln ("{}, {} {:d2} {:d2}:{:d2}:{:d2} GMT{}{:d2}:{:d2} {}", |
|---|
| 31 | | date.asDay, |
|---|
| 32 | | date.asMonth, |
|---|
| 33 | | date.day, |
|---|
| 34 | | date.hour, |
|---|
| 35 | | date.min, |
|---|
| 36 | | date.sec, |
|---|
| | 31 | dt.date.asDay, |
|---|
| | 32 | dt.date.asMonth, |
|---|
| | 33 | dt.date.day, |
|---|
| | 34 | dt.time.hours, |
|---|
| | 35 | dt.time.minutes, |
|---|
| | 36 | dt.time.seconds, |
|---|
| 37 | 37 | sign, |
|---|
| 38 | 38 | tz / 60, |
|---|
| 39 | 39 | tz % 60, |
|---|
| 40 | | date.year |
|---|
| | 40 | dt.date.year |
|---|
| 41 | 41 | ); |
|---|
| 42 | 42 | } |
|---|
| r2979 |
r3033 |
|
| 96 | 96 | |
|---|
| 97 | 97 | Stdout("!! Exporting from working copy for packaging").newline; |
|---|
| 98 | | auto date = WallClock.toDate; |
|---|
| | 98 | auto dt = WallClock.toDate; |
|---|
| 99 | 99 | char[] datestr; |
|---|
| 100 | | datestr = Stdout.layout.sprint(new char[10], "{}{:2}{:2}", date.year, date.month, date.day); |
|---|
| | 100 | datestr = Stdout.layout.sprint(new char[10], "{}{:2}{:2}", dt.date.year, dt.date.month, dt.date.day); |
|---|
| 101 | 101 | char[] packdirdate = projname ~ "-src-SNAPSHOT-" ~ datestr; |
|---|
| 102 | 102 | char[] packdircurrent = projname ~ "-src-SNAPSHOT-CURRENT"; |
|---|
| r3025 |
r3033 |
|
| 42 | 42 | /****************************************************************************** |
|---|
| 43 | 43 | |
|---|
| 44 | | We use a Gregorian calendar for all date calculations |
|---|
| 45 | | |
|---|
| 46 | | ******************************************************************************/ |
|---|
| 47 | | |
|---|
| 48 | | private alias GregorianCalendar Cal; |
|---|
| 49 | | |
|---|
| 50 | | /****************************************************************************** |
|---|
| 51 | | |
|---|
| 52 | 44 | Parse provided input and return a UTC epoch time. An exception |
|---|
| 53 | 45 | is raised where the provided string is not fully parsed. |
|---|
| … | … | |
| 151 | 143 | // convert time to field values |
|---|
| 152 | 144 | auto time = t.time; |
|---|
| 153 | | auto date = Cal.generic.toDate (t); |
|---|
| | 145 | auto date = GregorianCalendar.generic.toDate (t); |
|---|
| 154 | 146 | |
|---|
| 155 | 147 | // use the featherweight formatter ... |
|---|
| 156 | 148 | T[14] tmp = void; |
|---|
| 157 | 149 | return Util.layout (output, "%0, %1 %2 %3 %4:%5:%6 GMT", |
|---|
| 158 | | Days[date.dayOfWeek], |
|---|
| | 150 | Days[date.dow], |
|---|
| 159 | 151 | convert (tmp[0..2], date.day), |
|---|
| 160 | 152 | Months[date.month-1], |
|---|
| … | … | |
| 207 | 199 | { |
|---|
| 208 | 200 | TimeOfDay tod; |
|---|
| 209 | | Cal.Date date; |
|---|
| | 201 | Date date; |
|---|
| 210 | 202 | T* p = src.ptr; |
|---|
| 211 | 203 | |
|---|
| … | … | |
| 228 | 220 | p[0..3] == "GMT") |
|---|
| 229 | 221 | { |
|---|
| 230 | | value = Cal.generic.toTime (date, tod); |
|---|
| | 222 | value = GregorianCalendar.generic.toTime (date, tod); |
|---|
| 231 | 223 | return (p+3) - src.ptr; |
|---|
| 232 | 224 | } |
|---|
| … | … | |
| 248 | 240 | { |
|---|
| 249 | 241 | TimeOfDay tod; |
|---|
| 250 | | Cal.Date date; |
|---|
| | 242 | Date date; |
|---|
| 251 | 243 | T* p = src.ptr; |
|---|
| 252 | 244 | |
|---|
| … | … | |
| 275 | 267 | date.year += 1900; |
|---|
| 276 | 268 | |
|---|
| 277 | | value = Cal.generic.toTime (date, tod); |
|---|
| | 269 | value = GregorianCalendar.generic.toTime (date, tod); |
|---|
| 278 | 270 | return (p+3) - src.ptr; |
|---|
| 279 | 271 | } |
|---|
| … | … | |
| 295 | 287 | { |
|---|
| 296 | 288 | TimeOfDay tod; |
|---|
| 297 | | Cal.Date date; |
|---|
| | 289 | Date date; |
|---|
| 298 | 290 | T* p = src.ptr; |
|---|
| 299 | 291 | |
|---|
| … | … | |
| 315 | 307 | (date.year = parseInt (p)) > 0) |
|---|
| 316 | 308 | { |
|---|
| 317 | | value = Cal.generic.toTime (date, tod); |
|---|
| | 309 | value = GregorianCalendar.generic.toTime (date, tod); |
|---|
| 318 | 310 | return p - src.ptr; |
|---|
| 319 | 311 | } |
|---|
| … | … | |
| 334 | 326 | { |
|---|
| 335 | 327 | TimeOfDay tod; |
|---|
| 336 | | Cal.Date date; |
|---|
| | 328 | Date date; |
|---|
| 337 | 329 | T* p = src.ptr; |
|---|
| 338 | 330 | |
|---|
| … | … | |
| 362 | 354 | date.year += 1900; |
|---|
| 363 | 355 | |
|---|
| 364 | | value = Cal.generic.toTime (date, tod); |
|---|
| | 356 | value = GregorianCalendar.generic.toTime (date, tod); |
|---|
| 365 | 357 | return (p+2) - src.ptr; |
|---|
| 366 | 358 | } |
|---|
| … | … | |
| 381 | 373 | { |
|---|
| 382 | 374 | TimeOfDay tod; |
|---|
| 383 | | Cal.Date date; |
|---|
| | 375 | Date date; |
|---|
| 384 | 376 | T* p = src.ptr; |
|---|
| 385 | 377 | |
|---|
| … | … | |
| 399 | 391 | { |
|---|
| 400 | 392 | date.ms = parseInt (p); |
|---|
| 401 | | value = Cal.generic.toTime (date, tod); |
|---|
| | 393 | value = GregorianCalendar.generic.toTime (date, tod); |
|---|
| 402 | 394 | return p - src.ptr; |
|---|
| 403 | 395 | } |
|---|
| r2986 |
r3033 |
|
| 13 | 13 | module tango.time.Clock; |
|---|
| 14 | 14 | |
|---|
| | 15 | public import tango.time.Time; |
|---|
| | 16 | |
|---|
| 15 | 17 | private import tango.sys.Common; |
|---|
| 16 | 18 | |
|---|
| 17 | | private import tango.time.Date; |
|---|
| 18 | | |
|---|
| 19 | 19 | private import tango.core.Exception; |
|---|
| 20 | | |
|---|
| 21 | | public import tango.time.Time; |
|---|
| 22 | 20 | |
|---|
| 23 | 21 | /****************************************************************************** |
|---|
| … | … | |
| 58 | 56 | ***************************************************************/ |
|---|
| 59 | 57 | |
|---|
| 60 | | static Date toDate () |
|---|
| | 58 | static DateTime toDate () |
|---|
| 61 | 59 | { |
|---|
| 62 | 60 | return toDate (now); |
|---|
| … | … | |
| 75 | 73 | ***************************************************************/ |
|---|
| 76 | 74 | |
|---|
| 77 | | static Date toDate (Time time) |
|---|
| 78 | | { |
|---|
| 79 | | Date date = void; |
|---|
| | 75 | static DateTime toDate (Time time) |
|---|
| | 76 | { |
|---|
| | 77 | DateTime dt = void; |
|---|
| 80 | 78 | SYSTEMTIME sTime = void; |
|---|
| 81 | 79 | |
|---|
| … | … | |
| 83 | 81 | FileTimeToSystemTime (&fTime, &sTime); |
|---|
| 84 | 82 | |
|---|
| 85 | | date.year = sTime.wYear; |
|---|
| 86 | | date.month = sTime.wMonth; |
|---|
| 87 | | date.day = sTime.wDay; |
|---|
| 88 | | date.hour = sTime.wHour; |
|---|
| 89 | | date.min = sTime.wMinute; |
|---|
| 90 | | date.sec = sTime.wSecond; |
|---|
| 91 | | date.ms = sTime.wMilliseconds; |
|---|
| 92 | | date.dow = sTime.wDayOfWeek; |
|---|
| 93 | | return date; |
|---|
| | 83 | dt.date.year = sTime.wYear; |
|---|
| | 84 | dt.date.month = sTime.wMonth; |
|---|
| | 85 | dt.date.day = sTime.wDay; |
|---|
| | 86 | dt.date.dow = sTime.wDayOfWeek; |
|---|
| | 87 | dt.date.doy = 0; |
|---|
| | 88 | dt.date.era = 0; |
|---|
| | 89 | dt.time.hours = sTime.wHour; |
|---|
| | 90 | dt.time.minutes = sTime.wMinute; |
|---|
| | 91 | dt.time.seconds = sTime.wSecond; |
|---|
| | 92 | dt.time.millis = sTime.wMilliseconds; |
|---|
| | 93 | return dt; |
|---|
| 94 | 94 | } |
|---|
| 95 | 95 | |
|---|
| … | … | |
| 107 | 107 | ***************************************************************/ |
|---|
| 108 | 108 | |
|---|
| 109 | | static Time fromDate (inout Date date) |
|---|
| | 109 | static Time fromDate (inout DateTime dt) |
|---|
| 110 | 110 | { |
|---|
| 111 | 111 | SYSTEMTIME sTime = void; |
|---|
| 112 | 112 | FILETIME fTime = void; |
|---|
| 113 | 113 | |
|---|
| 114 | | sTime.wYear = cast(ushort) date.year; |
|---|
| 115 | | sTime.wMonth = cast(ushort) date.month; |
|---|
| | 114 | sTime.wYear = cast(ushort) dt.date.year; |
|---|
| | 115 | sTime.wMonth = cast(ushort) dt.date.month; |
|---|
| 116 | 116 | sTime.wDayOfWeek = 0; |
|---|
| 117 | | sTime.wDay = cast(ushort) date.day; |
|---|
| 118 | | sTime.wHour = cast(ushort) date.hour; |
|---|
| 119 | | sTime.wMinute = cast(ushort) date.min; |
|---|
| 120 | | sTime.wSecond = cast(ushort) date.sec; |
|---|
| 121 | | sTime.wMilliseconds = cast(ushort) date.ms; |
|---|
| | 117 | sTime.wDay = cast(ushort) dt.date.day; |
|---|
| | 118 | sTime.wHour = cast(ushort) dt.time.hours; |
|---|
| | 119 | sTime.wMinute = cast(ushort) dt.time.minutes; |
|---|
| | 120 | sTime.wSecond = cast(ushort) dt.time.seconds; |
|---|
| | 121 | sTime.wMilliseconds = cast(ushort) dt.time.millis; |
|---|
| 122 | 122 | |
|---|
| 123 | 123 | SystemTimeToFileTime (&sTime, &fTime); |
|---|
| … | … | |
| 178 | 178 | ***************************************************************/ |
|---|
| 179 | 179 | |
|---|
| 180 | | static Date toDate () |
|---|
| | 180 | static DateTime toDate () |
|---|
| 181 | 181 | { |
|---|
| 182 | 182 | return toDate (now); |
|---|
| … | … | |
| 195 | 195 | **************************************************************/ |
|---|
| 196 | 196 | |
|---|
| 197 | | static Date toDate (Time time) |
|---|
| 198 | | { |
|---|
| 199 | | Date date = void; |
|---|
| | 197 | static DateTime toDate (Time time) |
|---|
| | 198 | { |
|---|
| | 199 | DateTime dt = void; |
|---|
| 200 | 200 | auto timeval = convert (time); |
|---|
| 201 | | date.ms = timeval.tv_usec / 1000; |
|---|
| | 201 | dt.time.millis = timeval.tv_usec / 1000; |
|---|
| 202 | 202 | |
|---|
| 203 | 203 | tm t = void; |
|---|
| 204 | 204 | gmtime_r (&timeval.tv_sec, &t); |
|---|
| 205 | 205 | |
|---|
| 206 | | date.year = t.tm_year + 1900; |
|---|
| 207 | | date.month = t.tm_mon + 1; |
|---|
| 208 | | date.day = t.tm_mday; |
|---|
| 209 | | date.hour = t.tm_hour; |
|---|
| 210 | | date.min = t.tm_min; |
|---|
| 211 | | date.sec = t.tm_sec; |
|---|
| 212 | | date.dow = t.tm_wday; |
|---|
| 213 | | return date; |
|---|
| | 206 | dt.date.year = t.tm_year + 1900; |
|---|
| | 207 | dt.date.month = t.tm_mon + 1; |
|---|
| | 208 | dt.date.day = t.tm_mday; |
|---|
| | 209 | dt.date.dow = t.tm_wday; |
|---|
| | 210 | dt.date.doy = 0; |
|---|
| | 211 | dt.date.era = 0; |
|---|
| | 212 | dt.time.hours = t.tm_hour; |
|---|
| | 213 | dt.time.minutes = t.tm_min; |
|---|
| | 214 | dt.time.seconds = t.tm_sec; |
|---|
| | 215 | return dt; |
|---|
| 214 | 216 | } |
|---|
| 215 | 217 | |
|---|
| … | … | |
| 227 | 229 | ***************************************************************/ |
|---|
| 228 | 230 | |
|---|
| 229 | | static Time fromDate (inout Date date) |
|---|
| | 231 | static Time fromDate (inout DateTime dt) |
|---|
| 230 | 232 | { |
|---|
| 231 | 233 | tm t = void; |
|---|
| 232 | 234 | |
|---|
| 233 | | t.tm_year = date.year - 1900; |
|---|
| 234 | | t.tm_mon = date.month - 1; |
|---|
| 235 | | t.tm_mday = date.day; |
|---|
| 236 | | t.tm_hour = date.hour; |
|---|
| 237 | | t.tm_min = date.min; |
|---|
| 238 | | t.tm_sec = date.sec; |
|---|
| | 235 | t.tm_year = dt.date.year - 1900; |
|---|
| | 236 | t.tm_mon = dt.date.month - 1; |
|---|
| | 237 | t.tm_mday = dt.date.day; |
|---|
| | 238 | t.tm_hour = dt.time.hours; |
|---|
| | 239 | t.tm_min = dt.time.minutes; |
|---|
| | 240 | t.tm_sec = dt.time.seconds; |
|---|
| 239 | 241 | |
|---|
| 240 | 242 | auto seconds = timegm (&t); |
|---|
| 241 | 243 | return Time.epoch1970 + |
|---|
| 242 | 244 | TimeSpan.seconds(seconds) + |
|---|
| 243 | | TimeSpan.millis(date.ms); |
|---|
| | 245 | TimeSpan.millis(dt.time.millis); |
|---|
| 244 | 246 | } |
|---|
| 245 | 247 | |
|---|
| … | … | |
| 294 | 296 | debug (Clock) |
|---|
| 295 | 297 | { |
|---|
| 296 | | void main() {} |
|---|
| | 298 | void main() |
|---|
| | 299 | { |
|---|
| | 300 | auto time = Clock.now; |
|---|
| | 301 | } |
|---|
| 297 | 302 | } |
|---|
| r3024 |
r3033 |
|
| 48 | 48 | /// Fields in date will either be correct (e.g. months will be >= 1 and <= 12) or zero. |
|---|
| 49 | 49 | |
|---|
| 50 | | size_t iso8601Date(T)(T[] src, ref Calendar.Date date, size_t expanded = 0) { |
|---|
| 51 | | ubyte dummy = void; |
|---|
| 52 | | T* p = src.ptr; |
|---|
| 53 | | return doIso8601Date(p, src, date, expanded, dummy); |
|---|
| 54 | | } |
|---|
| 55 | | |
|---|
| 56 | | private size_t doIso8601Date(T)(ref T* p, T[] src, ref Calendar.Date date, size_t expanded, out ubyte separators) |
|---|
| | 50 | size_t iso8601Date(T)(T[] src, ref Date date, size_t expanded = 0) { |
|---|
| | 51 | ubyte dummy = void; |
|---|
| | 52 | T* p = src.ptr; |
|---|
| | 53 | return doIso8601Date(p, src, date, expanded, dummy); |
|---|
| | 54 | } |
|---|
| | 55 | |
|---|
| | 56 | private size_t doIso8601Date(T)(ref T* p, T[] src, ref Date date, size_t expanded, out ubyte separators) |
|---|
| 57 | 57 | out { |
|---|
| 58 | | assert (!date.month || ( date.month >= 1 && date.month <= 12)); |
|---|
| 59 | | assert (!date.day || (date.month && date.day >= 1 && date.day <= daysPerMonth(date.month, date.year))); |
|---|
| | 58 | assert (!date.month || (date.month >= 1 && date.month <= 12)); |
|---|
| | 59 | assert (!date.day || (date.month && date.day >= 1 && date.day <= daysPerMonth(date.month, date.year))); |
|---|
| 60 | 60 | } body { |
|---|
| 61 | 61 | |
|---|
| … | … | |
| 63 | 63 | date.era = GregorianCalendar.AD_ERA; |
|---|
| 64 | 64 | |
|---|
| 65 | | size_t eaten() { return p - src.ptr; } |
|---|
| 66 | | bool done(T[] s) { return .done(eaten(), src.length, *p, s); } |
|---|
| 67 | | |
|---|
| 68 | | if (!parseYear(p, expanded, date.year)) |
|---|
| 69 | | return (date.year = 0); |
|---|
| 70 | | |
|---|
| 71 | | auto onlyYear = eaten(); |
|---|
| 72 | | |
|---|
| 73 | | // /([+-]Y{expanded})?(YYYY|YY)/ |
|---|
| 74 | | if (done("-0123W")) |
|---|
| 75 | | return onlyYear; |
|---|
| 76 | | |
|---|
| 77 | | if (accept(p, '-')) |
|---|
| 78 | | separators = true; |
|---|
| 79 | | |
|---|
| 80 | | if (accept(p, 'W')) { |
|---|
| 81 | | // (year)-Www-D |
|---|
| 82 | | |
|---|
| 83 | | T* p2 = p; |
|---|
| 84 | | |
|---|
| 85 | | int i = parseIntMax(p, 3u); |
|---|
| 86 | | |
|---|
| 87 | | if (i) if (p - p2 == 2) { |
|---|
| 88 | | |
|---|
| 89 | | // (year)-Www |
|---|
| 90 | | if (done("-")) { |
|---|
| 91 | | if (getMonthAndDayFromWeek(date, i)) |
|---|
| 92 | | return eaten(); |
|---|
| 93 | | |
|---|
| 94 | | // (year)-Www-D |
|---|
| 95 | | } else if (demand(p, '-')) |
|---|
| 96 | | if (getMonthAndDayFromWeek(date, i, *p++ - '0')) |
|---|
| 97 | | return eaten(); |
|---|
| 98 | | |
|---|
| 99 | | } else if (p - p2 == 3) |
|---|
| 100 | | // (year)WwwD |
|---|
| 101 | | if (getMonthAndDayFromWeek(date, i / 10, i % 10)) |
|---|
| 102 | | return eaten(); |
|---|
| 103 | | |
|---|
| 104 | | return onlyYear; |
|---|
| 105 | | } |
|---|
| 106 | | |
|---|
| 107 | | // next up, MM or MM[-]DD or DDD |
|---|
| 108 | | |
|---|
| 109 | | T* p2 = p; |
|---|
| 110 | | |
|---|
| 111 | | int i = parseIntMax(p); |
|---|
| 112 | | if (!i) |
|---|
| 113 | | return onlyYear; |
|---|
| 114 | | |
|---|
| 115 | | switch (p - p2) { |
|---|
| 116 | | case 2: |
|---|
| 117 | | date.month = i; |
|---|
| 118 | | |
|---|
| 119 | | if (!(date.month >= 1 && date.month <= 12)) { |
|---|
| 120 | | date.month = 0; |
|---|
| 121 | | return onlyYear; |
|---|
| 122 | | } |
|---|
| 123 | | |
|---|
| 124 | | auto onlyMonth = eaten(); |
|---|
| 125 | | |
|---|
| 126 | | // (year)-MM |
|---|
| 127 | | if (done("-")) |
|---|
| 128 | | return onlyMonth; |
|---|
| 129 | | |
|---|
| 130 | | // (year)-MM-DD |
|---|
| 131 | | if (!( |
|---|
| 132 | | demand(p, '-') && |
|---|
| 133 | | (date.day = parseIntMax(p, 2u)) != 0 && date.day <= daysPerMonth(date.month, date.year) |
|---|
| 134 | | )) { |
|---|
| 135 | | date.day = 0; |
|---|
| 136 | | return onlyMonth; |
|---|
| 137 | | } |
|---|
| 138 | | |
|---|
| 139 | | break; |
|---|
| 140 | | |
|---|
| 141 | | case 4: |
|---|
| 142 | | // e.g. 20010203, i = 203 now |
|---|
| 143 | | |
|---|
| 144 | | date.month = i / 100; |
|---|
| 145 | | date.day = i % 100; |
|---|
| 146 | | |
|---|
| 147 | | // (year)MMDD |
|---|
| 148 | | if (!( |
|---|
| 149 | | date.month >= 1 && date.month <= 12 && |
|---|
| 150 | | date.day >= 0 && date.day <= daysPerMonth(date.month, date.year) |
|---|
| 151 | | )) { |
|---|
| 152 | | date.month = date.day = 0; |
|---|
| 153 | | return onlyYear; |
|---|
| 154 | | } |
|---|
| 155 | | |
|---|
| 156 | | break; |
|---|
| 157 | | |
|---|
| 158 | | case 3: |
|---|
| 159 | | // (year)-DDD |
|---|
| 160 | | // i is the ordinal of the day within the year |
|---|
| 161 | | |
|---|
| 162 | | bool leap = isLeapYear(date.year); |
|---|
| 163 | | |
|---|
| 164 | | if (i > 365 + leap) |
|---|
| 165 | | return onlyYear; |
|---|
| 166 | | |
|---|
| 167 | | if (i <= 31) { |
|---|
| 168 | | date.month = 1; |
|---|
| 169 | | date.day = i; |
|---|
| 170 | | |
|---|
| 171 | | } else if (i <= 59 + leap) { |
|---|
| 172 | | date.month = 2; |
|---|
| 173 | | date.day = i - 31 - leap; |
|---|
| 174 | | |
|---|
| 175 | | } else if (i <= 90 + leap) { |
|---|
| 176 | | date.month = 3; |
|---|
| 177 | | date.day = i - 59 - leap; |
|---|
| 178 | | |
|---|
| 179 | | } else if (i <= 120 + leap) { |
|---|
| 180 | | date.month = 4; |
|---|
| 181 | | date.day = i - 90 - leap; |
|---|
| 182 | | |
|---|
| 183 | | } else if (i <= 151 + leap) { |
|---|
| 184 | | date.month = 5; |
|---|
| 185 | | date.day = i - 120 - leap; |
|---|
| 186 | | |
|---|
| 187 | | } else if (i <= 181 + leap) { |
|---|
| 188 | | date.month = 6; |
|---|
| 189 | | date.day = i - 151 - leap; |
|---|
| 190 | | |
|---|
| 191 | | } else if (i <= 212 + leap) { |
|---|
| 192 | | date.month = 7; |
|---|
| 193 | | date.day = i - 181 - leap; |
|---|
| 194 | | |
|---|
| 195 | | } else if (i <= 243 + leap) { |
|---|
| 196 | | date.month = 8; |
|---|
| 197 | | date.day = i - 212 - leap; |
|---|
| 198 | | |
|---|
| 199 | | } else if (i <= 273 + leap) { |
|---|
| 200 | | date.month = 9; |
|---|
| 201 | | date.day = i - 243 - leap; |
|---|
| 202 | | |
|---|
| 203 | | } else if (i <= 304 + leap) { |
|---|
| 204 | | date.month = 10; |
|---|
| 205 | | date.day = i - 273 - leap; |
|---|
| 206 | | |
|---|
| 207 | | } else if (i <= 334 + leap) { |
|---|
| 208 | | date.month = 11; |
|---|
| 209 | | date.day = i - 304 - leap; |
|---|
| 210 | | |
|---|
| 211 | | } else { |
|---|
| 212 | | if (i > 365 + leap) |
|---|
| 213 | | assert (false); |
|---|
| 214 | | |
|---|
| 215 | | date.month = 12; |
|---|
| 216 | | date.day = i - 334 - leap; |
|---|
| 217 | | } |
|---|
| 218 | | |
|---|
| 219 | | default: break; |
|---|
| 220 | | } |
|---|
| 221 | | |
|---|
| 222 | | return eaten(); |
|---|
| | 65 | size_t eaten() { return p - src.ptr; } |
|---|
| | 66 | bool done(T[] s) { return .done(eaten(), src.length, *p, s); } |
|---|
| | 67 | |
|---|
| | 68 | if (!parseYear(p, expanded, date.year)) |
|---|
| | 69 | return (date.year = 0); |
|---|
| | 70 | |
|---|
| | 71 | auto onlyYear = eaten(); |
|---|
| | 72 | |
|---|
| | 73 | // /([+-]Y{expanded})?(YYYY|YY)/ |
|---|
| | 74 | if (done("-0123W")) |
|---|
| | 75 | return onlyYear; |
|---|
| | 76 | |
|---|
| | 77 | if (accept(p, '-')) |
|---|
| | 78 | separators = true; |
|---|
| | 79 | |
|---|
| | 80 | if (accept(p, 'W')) { |
|---|
| | 81 | // (year)-Www-D |
|---|
| | 82 | |
|---|
| | 83 | T* p2 = p; |
|---|
| | 84 | |
|---|
| | 85 | int i = parseIntMax(p, 3u); |
|---|
| | 86 | |
|---|
| | 87 | if (i) if (p - p2 == 2) { |
|---|
| | 88 | |
|---|
| | 89 | // (year)-Www |
|---|
| | 90 | if (done("-")) { |
|---|
| | 91 | if (getMonthAndDayFromWeek(date, i)) |
|---|
| | 92 | return eaten(); |
|---|
| | 93 | |
|---|
| | 94 | // (year)-Www-D |
|---|
| | 95 | } else if (demand(p, '-')) |
|---|
| | 96 | if (getMonthAndDayFromWeek(date, i, *p++ - '0')) |
|---|
| | 97 | return eaten(); |
|---|
| | 98 | |
|---|
| | 99 | } else if (p - p2 == 3) |
|---|
| | 100 | // (year)WwwD |
|---|
| | 101 | if (getMonthAndDayFromWeek(date, i / 10, i % 10)) |
|---|
| | 102 | return eaten(); |
|---|
| | 103 | |
|---|
| | 104 | return onlyYear; |
|---|
| | 105 | } |
|---|
| | 106 | |
|---|
| | 107 | // next up, MM or MM[-]DD or DDD |
|---|
| | 108 | |
|---|
| | 109 | T* p2 = p; |
|---|
| | 110 | |
|---|
| | 111 | int i = parseIntMax(p); |
|---|
| | 112 | if (!i) |
|---|
| | 113 | return onlyYear; |
|---|
| | 114 | |
|---|
| | 115 | switch (p - p2) { |
|---|
| | 116 | case 2: |
|---|
| | 117 | date.month = i; |
|---|
| | 118 | |
|---|
| | 119 | if (!(date.month >= 1 && date.month <= 12)) { |
|---|
| | 120 | date.month = 0; |
|---|
| | 121 | return onlyYear; |
|---|
| | 122 | } |
|---|
| | 123 | |
|---|
| | 124 | auto onlyMonth = eaten(); |
|---|
| | 125 | |
|---|
| | 126 | // (year)-MM |
|---|
| | 127 | if (done("-")) |
|---|
| | 128 | return onlyMonth; |
|---|
| | 129 | |
|---|
| | 130 | // (year)-MM-DD |
|---|
| | 131 | if (!( |
|---|
| | 132 | demand(p, '-') && |
|---|
| | 133 | (date.day = parseIntMax(p, 2u)) != 0 && date.day <= daysPerMonth(date.month, date.year) |
|---|
| | 134 | )) { |
|---|
| | 135 | date.day = 0; |
|---|
| | 136 | return onlyMonth; |
|---|
| | 137 | } |
|---|
| | 138 | |
|---|
| | 139 | break; |
|---|
| | 140 | |
|---|
| | 141 | case 4: |
|---|
| | 142 | // e.g. 20010203, i = 203 now |
|---|
| | 143 | |
|---|
| | 144 | date.month = i / 100; |
|---|
| | 145 | date.day = i % 100; |
|---|
| | 146 | |
|---|
| | 147 | // (year)MMDD |
|---|
| | 148 | if (!( |
|---|
| | 149 | date.month >= 1 && date.month <= 12 && |
|---|
| | 150 | date.day >= 0 && date.day <= daysPerMonth(date.month, date.year) |
|---|
| | 151 | )) { |
|---|
| | 152 | date.month = date.day = 0; |
|---|
| | 153 | return onlyYear; |
|---|
| | 154 | } |
|---|
| | 155 | |
|---|
| | 156 | break; |
|---|
| | 157 | |
|---|
| | 158 | case 3: |
|---|
| | 159 | // (year)-DDD |
|---|
| | 160 | // i is the ordinal of the day within the year |
|---|
| | 161 | |
|---|
| | 162 | bool leap = isLeapYear(date.year); |
|---|
| | 163 | |
|---|
| | 164 | if (i > 365 + leap) |
|---|
| | 165 | return onlyYear; |
|---|
| | 166 | |
|---|
| | 167 | if (i <= 31) { |
|---|
| | 168 | date.month = 1; |
|---|
| | 169 | date.day = i; |
|---|
| | 170 | |
|---|
| | 171 | } else if (i <= 59 + leap) { |
|---|
| | 172 | date.month = 2; |
|---|
| | 173 | date.day = i - 31 - leap; |
|---|
| | 174 | |
|---|
| | 175 | } else if (i <= 90 + leap) { |
|---|
| | 176 | date.month = 3; |
|---|
| | 177 | date.day = i - 59 - leap; |
|---|
| | 178 | |
|---|
| | 179 | } else if (i <= 120 + leap) { |
|---|
| | 180 | date.month = 4; |
|---|
| | 181 | date.day = i - 90 - leap; |
|---|
| | 182 | |
|---|
| | 183 | } else if (i <= 151 + leap) { |
|---|
| | 184 | date.month = 5; |
|---|
| | 185 | date.day = i - 120 - leap; |
|---|
| | 186 | |
|---|
| | 187 | } else if (i <= 181 + leap) { |
|---|
| | 188 | date.month = 6; |
|---|
| | 189 | date.day = i - 151 - leap; |
|---|
| | 190 | |
|---|
| | 191 | } else if (i <= 212 + leap) { |
|---|
| | 192 | date.month = 7; |
|---|
| | 193 | date.day = i - 181 - leap; |
|---|
| | 194 | |
|---|
| | 195 | } else if (i <= 243 + leap) { |
|---|
| | 196 | date.month = 8; |
|---|
| | 197 | date.day = i - 212 - leap; |
|---|
| | 198 | |
|---|
| | 199 | } else if (i <= 273 + leap) { |
|---|
| | 200 | date.month = 9; |
|---|
| | 201 | date.day = i - 243 - leap; |
|---|
| | 202 | |
|---|
| | 203 | } else if (i <= 304 + leap) { |
|---|
| | 204 | date.month = 10; |
|---|
| | 205 | date.day = i - 273 - leap; |
|---|
| | 206 | |
|---|
| | 207 | } else if (i <= 334 + leap) { |
|---|
| | 208 | date.month = 11; |
|---|
| | 209 | date.day = i - 304 - leap; |
|---|
| | 210 | |
|---|
| | 211 | } else { |
|---|
| | 212 | if (i > 365 + leap) |
|---|
| | 213 | assert (false); |
|---|
| | 214 | |
|---|
| | 215 | date.month = 12; |
|---|
| | 216 | date.day = i - 334 - leap; |
|---|
| | 217 | } |
|---|
| | 218 | |
|---|
| | 219 | default: break; |
|---|
| | 220 | } |
|---|
| | 221 | |
|---|
| | 222 | return eaten(); |
|---|
| 223 | 223 | } |
|---|
| 224 | 224 | |
|---|
| … | … | |
| 228 | 228 | /// time.hours may be 0 or 24: the latter marks the end of a day, the former the beginning. |
|---|
| 229 | 229 | |
|---|
| 230 | | size_t iso8601Time(T)(T[] src, ref Calendar.Date date, ref TimeOfDay time) { |
|---|
| 231 | | bool dummy = void; |
|---|
| 232 | | T* p = src.ptr; |
|---|
| 233 | | return doIso8601Time(p, src, date, time, WHATEVER, dummy); |
|---|
| | 230 | size_t iso8601Time(T)(T[] src, ref Date date, ref TimeOfDay time) { |
|---|
| | 231 | bool dummy = void; |
|---|
| | 232 | T* p = src.ptr; |
|---|
| | 233 | return doIso8601Time(p, src, date, time, WHATEVER, dummy); |
|---|
| 234 | 234 | } |
|---|
| 235 | 235 | |
|---|
| … | … | |
| 237 | 237 | |
|---|
| 238 | 238 | // bothValid is used only to get iso8601() to catch errors correctly |
|---|
| 239 | | private size_t doIso8601Time(T)(ref T* p, T[] src, ref Calendar.Date date, ref TimeOfDay time, ubyte separators, out bool bothValid) |
|---|
| | 239 | private size_t doIso8601Time(T)(ref T* p, T[] src, ref Date date, ref TimeOfDay time, ubyte separators, out bool bothValid) |
|---|
| 240 | 240 | out { |
|---|
| 241 | | // yes, I could just write >= 0, but this emphasizes the difference between == 0 and != 0 |
|---|
| 242 | | assert (!time.hours || (time.hours > 0 && time.hours <= 24)); |
|---|
| 243 | | assert (!time.minutes || (time.minutes > 0 && time.minutes <= 59)); |
|---|
| 244 | | assert (!time.seconds || (time.seconds > 0 && time.seconds <= 60)); |
|---|
| 245 | | assert (!time.millis || (time.millis > 0 && time.millis <= 999)); |
|---|
| | 241 | // yes, I could just write >= 0, but this emphasizes the difference between == 0 and != 0 |
|---|
| | 242 | assert (!time.hours || (time.hours > 0 && time.hours <= 24)); |
|---|
| | 243 | assert (!time.minutes || (time.minutes > 0 && time.minutes <= 59)); |
|---|
| | 244 | assert (!time.seconds || (time.seconds > 0 && time.seconds <= 60)); |
|---|
| | 245 | assert (!time.millis || (time.millis > 0 && time.millis <= 999)); |
|---|
| 246 | 246 | } body { |
|---|
| 247 | | size_t eaten() { return p - src.ptr; } |
|---|
| 248 | | bool done(T[] s) { return .done(eaten(), src.length, *p, s); } |
|---|
| 249 | | |
|---|
| 250 | | bool checkColon() { |
|---|
| 251 | | if (separators == WHATEVER) |
|---|
| 252 | | accept(p, ':'); |
|---|
| 253 | | |
|---|
| 254 | | else if (accept(p, ':') != separators) |
|---|
| 255 | | return false; |
|---|
| 256 | | |
|---|
| 257 | | return true; |
|---|
| 258 | | } |
|---|
| 259 | | |
|---|
| 260 | | byte getTimeZone() { return .getTimeZone(p, date, time, separators, &done); } |
|---|
| 261 | | |
|---|
| 262 | | // TODO/BUG: need to convert from local time if got T |
|---|
| 263 | | // however, Tango provides nothing like Phobos's std.date.getLocalTZA |
|---|
| 264 | | // (which doesn't look like it should work on Windows, it should use tzi.bias only, and GetTimeZoneInformationForYear) |
|---|
| 265 | | // (and which uses too complicated code for Posix, tzset should be enough) |
|---|
| 266 | | // and I'm not interested in delving into system-specific code right now |
|---|
| 267 | | // remember also that -1 BC is the year zero in ISO 8601... -2 BC is -1, etc |
|---|
| 268 | | if (separators == WHATEVER) |
|---|
| 269 | | accept(p, 'T'); |
|---|
| 270 | | |
|---|
| 271 | | if (parseInt(p, 2u, time.hours) != 2 || time.hours > 24) |
|---|
| 272 | | return (time.hours = 0); |
|---|
| 273 | | |
|---|
| 274 | | auto onlyHour = eaten(); |
|---|
| 275 | | |
|---|
| 276 | | // hh |
|---|
| 277 | | if (done("+,-.012345:")) |
|---|
| 278 | | return onlyHour; |
|---|
| 279 | | |
|---|
| 280 | | switch (getDecimal(p, time, HOUR)) { |
|---|
| 281 | | case NOTFOUND: break; |
|---|
| 282 | | case FOUND: |
|---|
| 283 | | auto onlyDecimal = eaten(); |
|---|
| 284 | | if (getTimeZone() == BAD) |
|---|
| 285 | | return onlyDecimal; |
|---|
| 286 | | |
|---|
| 287 | | // /hh,h+/ |
|---|
| 288 | | return eaten(); |
|---|
| 289 | | |
|---|
| 290 | | case BAD: return onlyHour; |
|---|
| 291 | | default: assert (false); |
|---|
| 292 | | } |
|---|
| 293 | | |
|---|
| 294 | | switch (getTimeZone()) { |
|---|
| 295 | | case NOTFOUND: break; |
|---|
| 296 | | case FOUND: return eaten(); |
|---|
| 297 | | case BAD: return onlyHour; |
|---|
| 298 | | default: assert (false); |
|---|
| 299 | | } |
|---|
| 300 | | |
|---|
| 301 | | if ( |
|---|
| 302 | | !checkColon() || |
|---|
| 303 | | |
|---|
| 304 | | parseInt(p, 2u, time.minutes) != 2 || time.minutes > 59 || |
|---|
| 305 | | |
|---|
| 306 | | // hour 24 is only for 24:00:00 |
|---|
| 307 | | (time.hours == 24 && time.minutes != 0) |
|---|
| 308 | | ) { |
|---|
| 309 | | time.minutes = 0; |
|---|
| 310 | | return onlyHour; |
|---|
| 311 | | } |
|---|
| 312 | | |
|---|
| 313 | | auto onlyMinute = eaten(); |
|---|
| 314 | | |
|---|
| 315 | | // hh:mm |
|---|
| 316 | | if (done("+,-.0123456:")) { |
|---|
| 317 | | bothValid = true; |
|---|
| 318 | | return onlyMinute; |
|---|
| 319 | | } |
|---|
| 320 | | |
|---|
| 321 | | switch (getDecimal(p, time, MINUTE)) { |
|---|
| 322 | | case NOTFOUND: break; |
|---|
| 323 | | case FOUND: |
|---|
| 324 | | auto onlyDecimal = eaten(); |
|---|
| 325 | | if (getTimeZone() == BAD) |
|---|
| 326 | | return onlyDecimal; |
|---|
| 327 | | |
|---|
| 328 | | // /hh:mm,m+/ |
|---|
| 329 | | bothValid = true; |
|---|
| 330 | | return eaten(); |
|---|
| 331 | | |
|---|
| 332 | | case BAD: return onlyMinute; |
|---|
| 333 | | default: assert (false); |
|---|
| 334 | | } |
|---|
| 335 | | |
|---|
| 336 | | switch (getTimeZone()) { |
|---|
| 337 | | case NOTFOUND: break; |
|---|
| 338 | | case FOUND: bothValid = true; return eaten(); |
|---|
| 339 | | case BAD: return onlyMinute; |
|---|
| 340 | | default: assert (false); |
|---|
| 341 | | } |
|---|
| 342 | | |
|---|
| 343 | | if ( |
|---|
| 344 | | !checkColon() || |
|---|
| 345 | | |
|---|
| 346 | | parseInt(p, 2u, time.seconds) != 2 || time.seconds > 60 || |
|---|
| 347 | | |
|---|
| 348 | | (time.hours == 24 && time.seconds != 0) || |
|---|
| 349 | | (time.seconds == 60 && time.hours != 23 && time.minutes != 59) |
|---|
| 350 | | ) { |
|---|
| 351 | | time.seconds = 0; |
|---|
| 352 | | return onlyMinute; |
|---|
| 353 | | } |
|---|
| 354 | | |
|---|
| 355 | | auto onlySecond = eaten(); |
|---|
| 356 | | |
|---|
| 357 | | // hh:mm:ss |
|---|
| 358 | | if (done("+,-.Z")) { |
|---|
| 359 | | bothValid = true; |
|---|
| 360 | | return onlySecond; |
|---|
| 361 | | } |
|---|
| 362 | | |
|---|
| 363 | | switch (getDecimal(p, time, SECOND)) { |
|---|
| 364 | | case NOTFOUND: break; |
|---|
| 365 | | case FOUND: |
|---|
| 366 | | auto onlyDecimal = eaten(); |
|---|
| 367 | | if (getTimeZone() == BAD) |
|---|
| 368 | | return onlyDecimal; |
|---|
| 369 | | |
|---|
| 370 | | // /hh:mm:ss,s+/ |
|---|
| 371 | | bothValid = true; |
|---|
| 372 | | return eaten(); |
|---|
| 373 | | |
|---|
| 374 | | case BAD: return onlySecond; |
|---|
| 375 | | default: assert (false); |
|---|
| 376 | | } |
|---|
| 377 | | |
|---|
| 378 | | if (getTimeZone() == BAD) |
|---|
| 379 | | return onlySecond; |
|---|
| 380 | | else { |
|---|
| 381 | | bothValid = true; |
|---|
| 382 | | return eaten(); // hh:mm:ss with timezone |
|---|
| 383 | | } |
|---|
| | 247 | size_t eaten() { return p - src.ptr; } |
|---|
| | 248 | bool done(T[] s) { return .done(eaten(), src.length, *p, s); } |
|---|
| | 249 | |
|---|
| | 250 | bool checkColon() { |
|---|
| | 251 | if (separators == WHATEVER) |
|---|
| | 252 | accept(p, ':'); |
|---|
| | 253 | |
|---|
| | 254 | else if (accept(p, ':') != separators) |
|---|
| | 255 | return false; |
|---|
| | 256 | |
|---|
| | 257 | return true; |
|---|
| | 258 | } |
|---|
| | 259 | |
|---|
| | 260 | byte getTimeZone() { return .getTimeZone(p, date, time, separators, &done); } |
|---|
| | 261 | |
|---|
| | 262 | // TODO/BUG: need to convert from local time if got T |
|---|
| | 263 | // however, Tango provides nothing like Phobos's std.date.getLocalTZA |
|---|
| | 264 | // (which doesn't look like it should work on Windows, it should use tzi.bias only, and GetTimeZoneInformationForYear) |
|---|
| | 265 | // (and which uses too complicated code for Posix, tzset should be enough) |
|---|
| | 266 | // and I'm not interested in delving into system-specific code right now |
|---|
| | 267 | // remember also that -1 BC is the year zero in ISO 8601... -2 BC is -1, etc |
|---|
| | 268 | if (separators == WHATEVER) |
|---|
| | 269 | accept(p, 'T'); |
|---|
| | 270 | |
|---|
| | 271 | if (parseInt(p, 2u, time.hours) != 2 || time.hours > 24) |
|---|
| | 272 | return (time.hours = 0); |
|---|
| | 273 | |
|---|
| | 274 | auto onlyHour = eaten(); |
|---|
| | 275 | |
|---|
| | 276 | // hh |
|---|
| | 277 | if (done("+,-.012345:")) |
|---|
| | 278 | return onlyHour; |
|---|
| | 279 | |
|---|
| | 280 | switch (getDecimal(p, time, HOUR)) { |
|---|
| | 281 | case NOTFOUND: break; |
|---|
| | 282 | case FOUND: |
|---|
| | 283 | auto onlyDecimal = eaten(); |
|---|
| | 284 | if (getTimeZone() == BAD) |
|---|
| | 285 | return onlyDecimal; |
|---|
| | 286 | |
|---|
| | 287 | // /hh,h+/ |
|---|
| | 288 | return eaten(); |
|---|
| | 289 | |
|---|
| | 290 | case BAD: return onlyHour; |
|---|
| | 291 | default: assert (false); |
|---|
| | 292 | } |
|---|
| | 293 | |
|---|
| | 294 | switch (getTimeZone()) { |
|---|
| | 295 | case NOTFOUND: break; |
|---|
| | 296 | case FOUND: return eaten(); |
|---|
| | 297 | case BAD: return onlyHour; |
|---|
| | 298 | default: assert (false); |
|---|
| | 299 | } |
|---|
| | 300 | |
|---|
| | 301 | if ( |
|---|
| | 302 | !checkColon() || |
|---|
| | 303 | |
|---|
| | 304 | parseInt(p, 2u, time.minutes) != 2 || time.minutes > 59 || |
|---|
| | 305 | |
|---|
| | 306 | // hour 24 is only for 24:00:00 |
|---|
| | 307 | (time.hours == 24 && time.minutes != 0) |
|---|
| | 308 | ) { |
|---|
| | 309 | time.minutes = 0; |
|---|
| | 310 | return onlyHour; |
|---|
| | 311 | } |
|---|
| | 312 | |
|---|
| | 313 | auto onlyMinute = eaten(); |
|---|
| | 314 | |
|---|
| | 315 | // hh:mm |
|---|
| | 316 | if (done("+,-.0123456:")) { |
|---|
| | 317 | bothValid = true; |
|---|
| | 318 | return onlyMinute; |
|---|
| | 319 | } |
|---|
| | 320 | |
|---|
| | 321 | switch (getDecimal(p, time, MINUTE)) { |
|---|
| | 322 | case NOTFOUND: break; |
|---|
| | 323 | case FOUND: |
|---|
| | 324 | auto onlyDecimal = eaten(); |
|---|
| | 325 | if (getTimeZone() == BAD) |
|---|
| | 326 | return onlyDecimal; |
|---|
| | 327 | |
|---|
| | 328 | // /hh:mm,m+/ |
|---|
| | 329 | bothValid = true; |
|---|
| | 330 | return eaten(); |
|---|
| | 331 | |
|---|
| | 332 | case BAD: return onlyMinute; |
|---|
| | 333 | default: assert (false); |
|---|
| | 334 | } |
|---|
| | 335 | |
|---|
| | 336 | switch (getTimeZone()) { |
|---|
| | 337 | case NOTFOUND: break; |
|---|
| | 338 | case FOUND: bothValid = true; return eaten(); |
|---|
| | 339 | case BAD: return onlyMinute; |
|---|
| | 340 | default: assert (false); |
|---|
| | 341 | } |
|---|
| | 342 | |
|---|
| | 343 | if ( |
|---|
| | 344 | !checkColon() || |
|---|
| | 345 | parseInt(p, 2u, time.seconds) != 2 || time.seconds > 60 || |
|---|
| | 346 | (time.hours == 24 && time.seconds != 0) || |
|---|
| | 347 | (time.seconds == 60 && time.hours != 23 && time.minutes != 59) |
|---|
| | 348 | ) { |
|---|
| | 349 | time.seconds = 0; |
|---|
| | 350 | return onlyMinute; |
|---|
| | 351 | } |
|---|
| | 352 | |
|---|
| | 353 | auto onlySecond = eaten(); |
|---|
| | 354 | |
|---|
| | 355 | // hh:mm:ss |
|---|
| | 356 | if (done("+,-.Z")) { |
|---|
| | 357 | bothValid = true; |
|---|
| | 358 | return onlySecond; |
|---|
| | 359 | } |
|---|
| | 360 | |
|---|
| < |
|---|
|