Skip to content

Instantly share code, notes, and snippets.

@ipcjs
Last active February 19, 2021 10:01
Show Gist options
  • Save ipcjs/086e6c07ae0d95d67945355362687bf8 to your computer and use it in GitHub Desktop.
Save ipcjs/086e6c07ae0d95d67945355362687bf8 to your computer and use it in GitHub Desktop.
SimpleDateFormat is used to format/parse simple patterns.
import 'package:intl/intl.dart';
/// @see:
/// - [intl/#83](https://github.com/dart-lang/intl/issues/83)
/// - [intl/#112](https://github.com/dart-lang/intl/issues/112)
/// - [DateFormat]
/// Created by ipcjs on 2021/2/19.
class SimpleDateFormat {
SimpleDateFormat(this.pattern) : assert(pattern.isNotEmpty);
final String pattern;
List<_Field> _fieldsPrivate;
List<_Field> get _fields => _fieldsPrivate ??= _parsePattern(pattern);
List<_Field> _parsePattern(String pattern) {
assert(pattern.isNotEmpty);
final fields = <_Field>[];
var startIndex = 0;
var prevChar = pattern[startIndex];
for (var i = startIndex + 1; i < pattern.length; i++) {
final char = pattern[i];
if (char != prevChar) {
fields.add(_Field(pattern.substring(startIndex, i)));
startIndex = i;
}
prevChar = char;
}
fields.add(_Field(pattern.substring(startIndex)));
return fields;
}
/// @see [DateFormat.format]
String format(DateTime date) {
final sb = StringBuffer();
for (final field in _fields) {
sb.write(field.format(date));
}
return sb.toString();
}
/// @see [DateFormat.parse]
DateTime parse(String string, [bool utc = false]) {
final reader = _StringReader(string);
final builder = _DateTimeBuilder()..utc = utc;
for (final field in _fields) {
field.parse(reader, builder);
}
return builder.build();
}
@override
String toString() => 'SimpleDateFormat{pattern: $pattern, fields: $_fields}';
}
/// @see [_DateFormatField]
class _Field {
_Field(this.pattern);
final String pattern;
int get width => pattern.length;
String _padInt(int v) => v.toString().padLeft(width, '0');
String format(DateTime date) {
switch (pattern[0]) {
case 'y':
return _padInt(date.year);
case 'M':
return _padInt(date.month);
case 'd':
return _padInt(date.day);
case 'H':
return _padInt(date.hour);
case 'm':
return _padInt(date.minute);
case 's':
return _padInt(date.second);
default:
return pattern;
}
}
int _readInt(_StringReader reader) => int.tryParse(reader.read(width)) ?? throwFormatException(reader);
void parse(_StringReader reader, _DateTimeBuilder builder) {
switch (pattern[0]) {
case 'y':
builder.year = _readInt(reader);
break;
case 'M':
builder.month = _readInt(reader);
break;
case 'd':
builder.day = _readInt(reader);
break;
case 'H':
builder.hour = _readInt(reader);
break;
case 'm':
builder.minute = _readInt(reader);
break;
case 's':
builder.second = _readInt(reader);
break;
default:
reader.skip(width);
break;
}
}
int /*Never*/ throwFormatException(_StringReader reader) {
throw FormatException('Trying to read $this from ${reader.contents} at position ${reader.index}');
}
@override
String toString() => pattern;
}
/// @see [IntlStream]
class _StringReader {
_StringReader(this.contents);
final String contents;
int index = 0;
String read(int width) {
final substring = contents.substring(index, index + width);
index += width;
return substring;
}
void skip(int width) {
index += width;
}
}
/// @see [DateBuilder]
class _DateTimeBuilder {
int year;
int month = 1;
int day = 1;
int hour = 0;
int minute = 0;
int second = 0;
int millisecond = 0;
int microsecond = 0;
bool utc = false;
DateTime build() => utc
? DateTime.utc(year, month, day, hour, minute, second, millisecond, microsecond)
: DateTime(year, month, day, hour, minute, second, millisecond, microsecond);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment