Constructs a new DateTime instance based on formattedString
.
Throws a FormatException if the input cannot be parsed.
The function parses a subset of ISO 8601 which includes the subset accepted by RFC 3339.
The accepted inputs are currently:
- A date: A signed four-to-six digit year, two digit month and
two digit day, optionally separated by
-
characters. Examples: "19700101", "-0004-12-24", "81030-04-01". - An optional time part, separated from the date by either
T
or a space. The time part is a two digit hour, then optionally a two digit minutes value, then optionally a two digit seconds value, and then optionally a '.' followed by a one-to-six digit second fraction. The minutes and seconds may be separated from the previous parts by a ':'. Examples: "12", "12:30:24.124", "123010.50". - An optional time-zone offset part, possibly separated from the previous by a space. The time zone is either 'z' or 'Z', or it is a signed two digit hour part and an optional two digit minute part. The sign must be either "+" or "-", and can not be omitted. The minutes may be separated from the hours by a ':'. Examples: "Z", "-10", "01:30", "1130".
This includes the output of both toString and toIso8601String, which
will be parsed back into a DateTime
object with the same time as the
original.
The result is always in either local time or UTC. If a time zone offset other than UTC is specified, the time is converted to the equivalent UTC time.
Examples of accepted strings:
"2012-02-27 13:27:00"
"2012-02-27 13:27:00.123456z"
"20120227 13:27:00"
"20120227T132700"
"20120227"
"+20120227"
"2012-02-27T14Z"
"2012-02-27T14+00:00"
"-123450101 00:00:00 Z"
: in the year -12345."2002-02-27T14:00:00-0500"
: Same as"2002-02-27T19:00:00Z"
Source
// TODO(lrn): restrict incorrect values like 2003-02-29T50:70:80. // Or not, that may be a breaking change. static DateTime parse(String formattedString) { /* * date ::= yeardate time_opt timezone_opt * yeardate ::= year colon_opt month colon_opt day * year ::= sign_opt digit{4,6} * colon_opt :: <empty> | ':' * sign ::= '+' | '-' * sign_opt ::= <empty> | sign * month ::= digit{2} * day ::= digit{2} * time_opt ::= <empty> | (' ' | 'T') hour minutes_opt * minutes_opt ::= <empty> | colon_opt digit{2} seconds_opt * seconds_opt ::= <empty> | colon_opt digit{2} millis_opt * micros_opt ::= <empty> | '.' digit{1,6} * timezone_opt ::= <empty> | space_opt timezone * space_opt :: ' ' | <empty> * timezone ::= 'z' | 'Z' | sign digit{2} timezonemins_opt * timezonemins_opt ::= <empty> | colon_opt digit{2} */ final RegExp re = new RegExp( r'^([+-]?\d{4,6})-?(\d\d)-?(\d\d)' // Day part. r'(?:[ T](\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d{1,6}))?)?)?' // Time part. r'( ?[zZ]| ?([-+])(\d\d)(?::?(\d\d))?)?)?$'); // Timezone part. Match match = re.firstMatch(formattedString); if (match != null) { int parseIntOrZero(String matched) { if (matched == null) return 0; return int.parse(matched); } // Parses fractional second digits of '.(\d{1,6})' into the combined // microseconds. int parseMilliAndMicroseconds(String matched) { if (matched == null) return 0; int length = matched.length; assert(length >= 1); assert(length <= 6); int result = 0; for (int i = 0; i < 6; i++) { result *= 10; if (i < matched.length) { result += matched.codeUnitAt(i) ^ 0x30; } } return result; } int years = int.parse(match[1]); int month = int.parse(match[2]); int day = int.parse(match[3]); int hour = parseIntOrZero(match[4]); int minute = parseIntOrZero(match[5]); int second = parseIntOrZero(match[6]); bool addOneMillisecond = false; int milliAndMicroseconds = parseMilliAndMicroseconds(match[7]); int millisecond = milliAndMicroseconds ~/ Duration.MICROSECONDS_PER_MILLISECOND; int microsecond = milliAndMicroseconds.remainder(Duration.MICROSECONDS_PER_MILLISECOND); bool isUtc = false; if (match[8] != null) { // timezone part isUtc = true; if (match[9] != null) { // timezone other than 'Z' and 'z'. int sign = (match[9] == '-') ? -1 : 1; int hourDifference = int.parse(match[10]); int minuteDifference = parseIntOrZero(match[11]); minuteDifference += 60 * hourDifference; minute -= sign * minuteDifference; } } int value = _brokenDownDateToValue( years, month, day, hour, minute, second, millisecond, microsecond, isUtc); if (value == null) { throw new FormatException("Time out of range", formattedString); } return new DateTime._withValue(value, isUtc: isUtc); } else { throw new FormatException("Invalid date format", formattedString); } }