I am writing an app that needs to be quite accurate in dates and I wonder how can I compare LocalDate instances.. for now I was using something like:
LocalDate localdate1 = LocalDate().now();
LocalDate localdate2 = someService.getSomeDate();
localdate1.equals(localdate2);
But I noticed that my app is giving me some confusing results, and I think it is because of the date comparing.
I am thinking about obtaining the time from 1970' in long and compare those two, but I must be easier, I am sure of it
Using equals()
LocalDate does override equals:
int compareTo0(LocalDate otherDate) {
int cmp = (year - otherDate.year);
if (cmp == 0) {
cmp = (month - otherDate.month);
if (cmp == 0) {
cmp = (day - otherDate.day);
}
}
return cmp;
}
If you are not happy with the result of equals(), you are good using the predefined methods of LocalDate.
isAfter()
isBefore()
isEqual()
Notice that all of those method are using the compareTo0() method and just check the cmp value. if you are still getting weird result (which you shouldn't), please attach an example of input and output
LocalDate ld ....;
LocalDateTime ldtime ...;
ld.isEqual(LocalDate.from(ldtime));
I believe this snippet will also be helpful in a situation where the dates comparison spans more than two entries.
static final int COMPARE_EARLIEST = 0;
static final int COMPARE_MOST_RECENT = 1;
public LocalDate getTargetDate(List<LocalDate> datesList, int comparatorType) {
LocalDate refDate = null;
switch(comparatorType)
{
case COMPARE_EARLIEST:
//returns the most earliest of the date entries
refDate = (LocalDate) datesList.stream().min(Comparator.comparing(item ->
item.toDateTimeAtCurrentTime())).get();
break;
case COMPARE_MOST_RECENT:
//returns the most recent of the date entries
refDate = (LocalDate) datesList.stream().max(Comparator.comparing(item ->
item.toDateTimeAtCurrentTime())).get();
break;
}
return refDate;
}
Related
I guess it is not possible to parse a date in "MMddyy" format in dart.
void main() {
String strcandidate = "031623";
String format = "MMddyy";
var originalFormat = DateFormat(format).parse(strcandidate);
}
Output:
Uncaught Error: FormatException: Trying to read dd from 031623 at position 6
The following works fine when parsing a date in "MM-dd-yy" format.
void main() {
String strcandidate = "03-16-23";
String format = "MM-dd-yy";
var originalFormat = DateFormat(format).parse(strcandidate);
}
In the problem, the input date string can be in any format e.g ['yyyy-MM-dd', 'MMM'-yyyy, 'MM/dd/yy']. I am parsing the input string for these formats in a loop as follows.
dateFormatsList = ['yyyy-MM-dd', 'MMM'-yyyy, 'MM/dd/yy'];
for (String format in dateFormatsList ) {
try {
originalFormat = DateFormat(format).parse(strcandidate);
dateFound = true;
} catch (e) {}
}
Adding 'MMddyy' to dateFormatsList is not going to work.
But regular expression be used to parse this format.
However if all formats are parsed using parse method and one additional format is parsed using regular expression, then the code is not that neat, and cluttered.
To write as much neat and efficient code as possible, if you want, you can share your insights about any possibility for making it efficient and clean while incorporating 'MMddyy'format. Tysm!
See How do I convert a date/time string to a DateTime object in Dart? for how to parse various date/time strings to DateTime objects.
If you need to mix approaches, you can provide a unified interface. Instead of using a List<String> for your list of formats, you can use a List<DateTime Function(String)>:
import 'package:intl/intl.dart';
/// Parses a [DateTime] from [dateTimeString] using a [RegExp].
///
/// [re] must have named groups with names `year`, `month`, and `day`.
DateTime parseDateFromRegExp(RegExp re, String dateTimeString) {
var match = re.firstMatch(dateTimeString);
if (match == null) {
throw FormatException('Failed to parse: $dateTimeString');
}
var year = match.namedGroup('year');
var month = match.namedGroup('month');
var day = match.namedGroup('day');
if (year == null || month == null || day == null) {
throw ArgumentError('Regular expression is malformed');
}
// In case we're parsing a two-digit year format, instead of
// parsing the strings ourselves, reparse it with [DateFormat] so that it can
// apply its -80/+20 rule.
//
// [DateFormat.parse] doesn't work without separators, which is why we
// can't use directly on the original string. See:
// https://github.com/dart-lang/intl/issues/210
return DateFormat('yy-MM-dd').parse('$year-$month-$day');
}
typedef DateParser = DateTime Function(String);
DateParser dateParserFromRegExp(String rePattern) =>
(string) => parseDateFromRegExp(RegExp(rePattern), string);
var parserList = [
DateFormat('yyyy-MM-dd').parse,
DateFormat('MMM-yyyy').parse,
DateFormat('MM/dd/yy').parse,
dateParserFromRegExp(
r'^(?<month>\d{2})(?<day>\d{2})(?<year>\d{4})$',
)
];
void main() {
var strcandidate = '12311776';
DateTime? originalFormat;
for (var tryParse in parserList) {
try {
originalFormat = tryParse(strcandidate);
break;
} on Exception {
// Try the next format.
}
}
print(originalFormat);
}
I think it's a bit hacky but what about use a regular expression (RegExp) to parse the date divider and then replace it with just ""?
void main() {
String strcandidate = "031623";
String strYear = strcandidate.substring(4);
//Taken 20 as the year like 2023 as year is in 2 digits
String _newDateTime = '20' + strYear + strcandidate.substring(0, 4);
var _originalFormat = DateTime.parse(_newDateTime);
print(_originalFormat);
}
add the intl to yaml then write this code:
import 'package:intl/intl.dart';
void main() {
var strcandidate = DateTime(2023, 3, 16);
String format = "MMddyy";
var originalFormat = DateFormat(format).format(strcandidate);
print(originalFormat);
}
I have a String date in format Month-Day-4DigitYear that I want to convert to DateTime in Flutter. I'm a novice coder, and I'm struggling to understand the api.flutter.dev Parse method example.
Below is the example. I just have a few issues. Android Studio throws multiple errors when I just create a class and put in this function. I think I understand the non-nullable issue, so I delete the ! and ? marks everywhere.
My issues are: what are _parseFormat, _brokenDownDateToValue, _withValue ?
All give errors and just declaring the first two and deleting the _withValue doesn't seem to do the trick, although removes all errors. It's like they've left out a key portion that I'm missing or there is a package I need to import the neither I nor Android Studio knows about. Can anyone decrypt this? I get very frustrated with flutter's documentation, as it always seems to give 80% of required info, assuming you already are clairvoyant on all other topics except this single one they are discussing. Gotta be a pro before reading the manual.
// TODO(lrn): restrict incorrect values like 2003-02-29T50:70:80.
// Or not, that may be a breaking change.
static DateTime parse(String formattedString) {
var re = _parseFormat;
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+)' into the combined
// microseconds. We only use the first 6 digits because of DateTime
// precision of 999 milliseconds and 999 microseconds.
int parseMilliAndMicroseconds(String? matched) {
if (matched == null) return 0;
int length = matched.length;
assert(length >= 1);
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]);
int milliAndMicroseconds = parseMilliAndMicroseconds(match[7]);
int millisecond =
milliAndMicroseconds ~/ Duration.microsecondsPerMillisecond;
int microsecond = milliAndMicroseconds
.remainder(Duration.microsecondsPerMillisecond) as int;
bool isUtc = false;
if (match[8] != null) {
// timezone part
isUtc = true;
String? tzSign = match[9];
if (tzSign != null) {
// timezone other than 'Z' and 'z'.
int sign = (tzSign == '-') ? -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 FormatException("Time out of range", formattedString);
}
return DateTime._withValue(value, isUtc: isUtc);
} else {
throw FormatException("Invalid date format", formattedString);
}
}
My issues are: what are _parseFormat, _brokenDownDateToValue, _withValue ?
These are objects or functions declared elsewhere in the lib which are private (the _ as the first character declares objects and functions as private) and therefore not shown in the documentation.
_parseFormat seems to be a regular expression.
_brokenDownDateToValue seems to be a function.
_withValue is a named constructor.
I think what you want to use is the following if you want to parse your date String to a DateTime object.
var date = "11-28-2020"; // Month-Day-4DigitYear
var dateTime = DateTime.parse(date.split('-').reversed.join());
See https://api.flutter.dev/flutter/dart-core/DateTime/parse.html for the accepted strings to be parsed.
I did find the full code example here.
It didn't use the name _parseFormat, instead just RegExp? And has _withValue and _brokenDownDateToValue declarations.
As I see it, there isn't a proper way to decode their example. The example is insufficient. A dictionary should not create definitions using words that can't be found elsewhere in the dictionary.
Dart/Flutter - How to avoid auto rounding done by NumberFormat.compactCurrency(locale: "en_IN").format() method while formatting a value?
num value = 29886964;
NumberFormat numberFormat = NumberFormat.compactCurrency(locale: "en_IN");
String output = numberFormat.format(value);
Actual output = INR2.99Cr
Requirement/Expectation: INR2.98Cr
use NumberFormat.currency api
num value = 29886964;
NumberFormat numberFormat = NumberFormat.compactCurrency(locale: "en_IN" );
final count = math.pow(10 , (value.toString().length - numberFormat.significantDigits));
var result = value / count;
String output = numberFormat.format(result.floor() * count);
print(output);
value : INR2.98Cr
The Accepted answer didn't work for me, unfortunately.
So, this is my answer.
extension DoubleExtension on double {
String get formattedCurrency {
final formatter = NumberFormat.currency(symbol: '\$', decimalDigits: 2);
return formatter.format(this);
}
// Another way around
String get formattedCurrency_2 {
final formatter = NumberFormat('###.0#');
return formatter.format(this);
}
}
and you can use the extension this way:
<your_double_number>.formattedCurrency;
Don't forget to import the extension in your class.
For more info about the configuration please look at this document:
https://api.flutter.dev/flutter/intl/NumberFormat-class.html
I have json response which comes in different languages and I want to compare day; when I choose language Turkish it gets a response like:
sunday = {
day = Pazar;
timings = (
{
endTime = "23:59:00";
id = 100000174;
startTime = "11:58:00";
}
);
};
wednesday = {
day = "\U00e7ar\U015famba";
timings = (
{
endTime = "22:00:00";
id = 100000177;
startTime = "13:00:00";
}
);
};
I want to compare them the following way:
if(day.lowercased() == “\U00e7ar\U015famba”) {
//get Wednesday day
//this condition work in Sunday Dictionary
}
But It shows me error “Invalid escape sequence” and “closure expression is unused” Please see this image.
In Swift, hexadecimal value after the \u escape sequence needs to be enclosed in braces (with the \u as lowercase):
"\u{00e7}ar\u{015f}amba"
That's the reason for the error you're getting.
However, if you're comparing with the values returned from your JSON, which don't follow this format, you'll need to double-escape the \U to compare them as plain strings:
if day.lowercased() == "\\U00e7ar\\U015famba" {
...
}
I am attempting to parse a date string of (almost) arbitrary length. The approach I had with SimpleDateFormat was something like this
private Date parseWithSimpleDateFormat(String dateString) throws ParseException {
String pattern = "yyyyMMddHHmmss".substring(0, dateString.length());
SimpleDateFormat format = new SimpleDateFormat(pattern);
return format.parse(dateString);
}
... which I want to do "better" with the new Date API. What I've come up with is the following
private static final DateTimeFormatter FLEXIBLE_FORMATTER = new DateTimeFormatterBuilder()
.appendPattern("yyyy[MM[dd[HH[mm[ss]]]]]")
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter();
private Date parseWithDateTimeFormatter(String dateString) {
LocalDateTime localDateTime = LocalDateTime.parse(dateString, FLEXIBLE_FORMATTER);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
Instant instant = zonedDateTime.toInstant();
return Date.from(instant);
}
with the following outcome
parseWithDateTimeFormatter("2016"); // works as intended
parseWithDateTimeFormatter("201605"); // Text '201605' could not be parsed at index 0
parseWithDateTimeFormatter("20160504"); // Text '20160504' could not be parsed at index 0
parseWithDateTimeFormatter("2016050416"); // Text '2016050416' could not be parsed at index 0
parseWithDateTimeFormatter("201605041636"); // Text '201605041636' could not be parsed at index 0
What am I doing wrong here, or how would I further troubleshoot this?
You can use this modified formatter in order to avoid parsing more than 4 digits for the year:
private static final DateTimeFormatter FLEXIBLE_FORMATTER =
new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendPattern("[MM[dd[HH[mm[ss]]]]]")
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
.toFormatter();
In contrast to other fields like month (MM) etc., the year field symbol y has no limitation to four digits as indicated by count of y-letters.