Dart - Convert String to DateTime

This tutorial gives you examples of how to convert a String to a DateTime in Dart (also works in Flutter) using DateTime.parse, HttpDate.pasre, and DateFormat().parse().

Dart uses DateTime as data type to represent a time instant. Storing a time instant as DateTime has many advantages compared to storing the data as a String, such as the ability to compare with other time instant and the simplicity to add a certain amount of time to the instant. If you have the value as String and you want to convert it to a DateTime instance, Dart already provides some methods. Below are the methods that can be used for the conversion, along with explanation and examples for each method.

Using DateTime.parse

DateTime has a static method parse() that can be used to parse values with certain formats.

  static DateTime parse(String formattedString)

The method can be used by importing package:intl/intl.dart. It creates a new DateTime based on the given string. The passed value must not be null. A FormatException will be thrown if the passed value cannot be parsed.

It supports a subset of ISO 8601 including the subset accepted by RFC 3339. The supported formats consist of up to three parts: date, optional time, and optional time-zone offset. The only required part is date, while the others are optional. That means you can pass date only, date + time, or date + time + time-zone offset. Between date and time needs to be separated using "T" or space.

The date must contain year, month, and day with each unit can be separated with optional dash (-). The year can have 4 to 6 digits and negative sign (-) can be added to represent B.C. years. Examples: "2020-01-02" "20200102", "-12345-03-04"

The time part is optional. If present, it consists of 2-digit hour, optional 2-digit minute, optional 2-digit second, and optional dot (.) or comma (,) followed by one or more digit second fraction. Between hour, minute, and second can be separated by optional colon (:). Examples: "07", 0712", "071250", "071250,1111", "07:12:50.222222".

The time-zone offset part is optional. If present, the value can be 'Z' (or 'z') or a 2-digit hour part followed by optional 2-digit minute part separated by optional colon (:). Examples: "Z", "z", "+07", "+0700", "+07:00". If you pass a non-UTC time-zone, the result will be converted to UTC time.

To use DateTime.parse, you only need to pass the string you want to convert.

  DateTime dt = DateTime.parse('2020-01-02 03:04:05');
  print(dt); // 2020-01-02 03:04:05.000

Based on the explanation above, below are some examples of valid inputs along with the outputs.

  print(DateTime.parse('2020-01-02')); // 2020-01-02 00:00:00.000
  print(DateTime.parse('20200102')); // 2020-01-02 00:00:00.000
  print(DateTime.parse('-12345-03-04')); // -12345-03-04 00:00:00.000
  print(DateTime.parse('2020-01-02 07')); // 2020-01-02 07:00:00.000
  print(DateTime.parse('2020-01-02T07')); // 2020-01-02 07:00:00.000
  print(DateTime.parse('2020-01-02T07:12')); // 2020-01-02 07:12:00.000
  print(DateTime.parse('2020-01-02T07:12:50')); // 2020-01-02 07:12:50.000
  print(DateTime.parse('2020-01-02T07:12:50Z')); // 2020-01-02 07:12:50.000Z
  print(DateTime.parse('2020-01-02T07:12:50+07')); // 2020-01-02 00:12:50.000Z
  print(DateTime.parse('2020-01-02T07:12:50+0700')); // 2020-01-02 00:12:50.00
  print(DateTime.parse('2020-01-02T07:12:50+07:00')); // 2020-01-02 00:12:50.00

What if you pass an invalid value. For example, the passed value below only contains the year without month and day.

  print(DateTime.parse('2020'));

Because the passed value is invalid, it will throw FormatException: Invalid date format.

Besides invalid value, you cannot pass null value.

  print(DateTime.parse(null));

The result will be an exception Invalid argument(s) (input): Must not be null

 

Using DateTime.tryParse

Because DateTime.parse will throw FormatException for invalid value, you may need to catch the exception. Dart already provides another static method DateTime.tryParse. It wraps DateTime.parse inside a try-catch block. If FormatException is thrown, it will return null.

  static DateTime parse(String formattedString)

Below is an example of using tryParse with the previous invalid value.

  print(DateTime.tryParse('2020')); // null

However, you cannot pass null value as it will throw the same exception as using parse().

 

Using HttpDate.parse

If the given date complies with HTTP date formats according to RFC-1123, RFC-850, or ANSI C's asctime(), you can use HttpDate.parse.

  static DateTime parse(String formattedString)

You need to import dart:io first to use the method. The formats supported by the method are:

  • EEE, dd MMM yyyy HH:mm:ss 'GMT'
  • EEEE, dd-MMM-yyyy HH:mm:ss 'GMT'
  • EEE MMM dd HH:mm:ss yyyy

Below are some examples that comply with the supported formats.

  print(HttpDate.parse("Wed, 28 Oct 2020 01:02:03 GMT")); // 2020-10-28 01:02:03.000Z
  print(HttpDate.parse("Wednesday, 28-Oct-2020 01:02:03 GMT")); // 2020-10-28 01:02:03.000Z
  print(HttpDate.parse("Wed Oct 28 01:02:03 2020")); // 2020-10-28 01:02:03.000Z

What will happen if you pass an invalid date?

  HttpDate.parse("Wed- 2020-Oct-28-01:02:03")

The above example will throw HttpException: Invalid HTTP date Wed- 2020-Oct-28-01:02:03. Unfortunately, there is no tryParse method for HttpDate which means you have to catch the exception yourself. It will also throw an exception if you pass null value.

 

Using DateFormat

If you need to parse a String whose format is not supported by DateTime.parse or HttpDate.parse, you can use DateFormat and define the pattern of the String you want to parse.

What you need to do is creating a DateFormat by passing the pattern in the constructor, then call DateFormat's parse() instance method by passing the string to be parsed. To use the method, it's required to import intl/intl.dart.

  DateTime parse(String inputString, [utc = false])

Below are the characters supported by DateFormat.

  • G: Era designator (text). Examples: AD, BC.
  • y: Year (number). Examples: 2020.
  • M: Month in year (text & number). Examples: 01 (M/MM), Jan (MMM), January (MMMM).
  • L: Standalone month (text & number). Examples: 01 (L/LL), Jan (LLL), January (LLLL).
  • d: Day in month (number). Examples: 10.
  • c: Standalone day (number). Examples: 10.
  • h: Hour in am/pm (1~12) (number). Examples: 12.
  • H: Hour in day (0~23) (number). Examples: 0.
  • m: Minute in hour (number). Examples: 20.
  • s: Second in minute (number). Examples: 30.
  • S: Fractional second (number). Examples: 123.
  • E: Day of week (text). Examples: Sunday.
  • D: Day in year (number). Examples: 200.
  • a: am/pm marker (text). Examples: AM, PM.
  • k: Hour in day (1~24) (number). Examples: 24.
  • K: Hour in am/pm (0~11) (number). Examples: 0.
  • z: Time zone (text). Examples: Pacific Standard Time. Not yet implemented.
  • Z: Time zone (RFC 822) (number). Examples: +0700. Not yet implemented.
  • v: Time zone (generic) (text). Examples: Pacific Time. Not yet implemented.
  • Q: quarter (text). Examples: Q1.
  • ': Escape for text (delimiter). Examples: 'custom text'.
  • ": Single quote (literal). Examples: 'o''clock'.

The symbols above can have multi letters variations to define the expected form. For example, a symbol can have full form, abbreviated form, and numeric form which is distinguished by the number of letters.

Below is the rule for symbols that only supports text presentation.

  • < 3 letters: numeric form.
  • 3 letters: short/abbreviated form.
  • 4 or more letters: full form.
  • 5 letters: narrow form for standalone, otherwise not used.

If the symbol is numeric, the number of letters is used to define the number of digits. Though you can use yy while formatting DateTime to String for truncating the year into 2 digits, Dart treats 2 digits year in String as the number of years from 0 A.D.. That means 20 is converted to 0020, not 2020.

Beware in using the characters above to define a pattern because Dart hasn't implemented all the characters. The characters related to time zone (z, Z, v) haven't been implemented so far.

Because a DateTime must contain year, month, day, hour, minute, second, and second fraction, the default value will be set to each unit whose value cannot be obtained from the parsed String. The default value for year is 1970. For month and day, the default value is 1. 0 is the default value for hour, minute, second, and second fraction. Even you can pass an empty string which means every unit is set with default value.

  print(new DateFormat('').parse('')); // 1970-01-01 00:00:00.000
  print(new DateFormat('MM').parse('02')); // 1970-02-01 00:00:00.000
  print(new DateFormat('yyyy').parse('2020')); // 2020-01-01 00:00:00.000
  print(new DateFormat('MM/yyyy').parse('01/2020')); // 2020-01-01 00:00:00.000
  print(new DateFormat('MMM yyyy').parse('Feb 2020')); // 2020-02-01 00:00:00.000
  print(new DateFormat('MMMM yyyy').parse('March 2020')); // 2020-03-01 00:00:00.000
  print(new DateFormat('yyyy/MM/dd').parse('2020/04/03')); // 2020-04-03 00:00:00.000
  print(new DateFormat('yyyy/MM/dd HH:mm:ss').parse('2020/04/03 17:03:02')); // 2020-04-03 17:03:02.000
  print(new DateFormat('EEEE dd MMMM yyyy HH:mm:ss \'GMT\'').parse('Wednesday 28 October 2020 01:02:03 GMT')); // 2020-10-28 01:02:03.000
  print(new DateFormat('EEE d/LLLL/yyyy hh:mm:ss G').parse('Wed 10/January/2020 15:02:03 BC')); // 2020-01-10 15:02:03.000
  print(new DateFormat('yyyyy.MMMM.dd GGG hh:mm aaa').parse('02020.July.10 AD 11:09 PM')); // 2020-07-10 23:09:00.000

Passing a value that cannot be parsed (either invalid or null) like the examples below will cause the method to throw FormatException.

  print(new DateFormat('yyyy/MM/dd').parse('x'));
  print(new DateFormat('yyyy/MM/dd').parse(null));

 

That's how to parse a String to a DateTime in Dart. For basic formats, it should be quite easy to perform the conversion. However, there are still some limitations such as the lack of time zone support which requires a workaround for now.