Comparing date by combining it - date

Im just wondering. is it ok to combine years,month, and day of two date and make a comparison based on the combination.
eg:
Date A: 12th-January-2019
Date B: 24th-December-2018
Based on the above date, i could combine the year,month, and day as follow:
Date A: 20190112
Date B: 20181224
So based on the numbers, i could do logic like below to compare which date comes first:
if(Date A > Date B) {
output("Date A is the latest")
}
i would like to implement this method just to compare which is the latest date.
are there any problem of doing so.

java.time
Since you are using Java, I suggest that you take advantage of java.time, the modern Java date and time API.
String[] dateStringsFromDatabase = {
"2018/12/19",
"2017-02-01",
"2018.03.04",
"25-12-2016", // reversed
"2019\\09\\12",
"2014:03:01"
};
List<LocalDate> parsedDates = new ArrayList<>(dateStringsFromDatabase.length);
for (String dateString : dateStringsFromDatabase) {
// No matter which delimiter is used, replace it with a hyphen
String text = dateString.replaceAll("\\W", "-");
try {
parsedDates.add(LocalDate.parse(text));
} catch (DateTimeParseException dtpe) {
System.out.println(dateString + " not parsed: " + dtpe.getMessage());
}
}
Output:
25-12-2016 not parsed: Text '25-12-2016' could not be parsed at index 0
What this approach buys you is validation of the dates even though they come with all different delimiters. Especially in this situation I believe that you should want to validate that your strings are within the expected variations. Otherwise you risk that a date with the numbers reversed ends up as a date in year 2512, for example. You want to catch that before it happens.
Example of comparing which comes first:
for (int i = 1; i < parsedDates.size(); i++) {
LocalDate dateA = parsedDates.get(i - 1);
LocalDate dateB = parsedDates.get(i);
if (dateA.isAfter(dateB)) {
System.out.format("%s is later than %s%n", dateA, dateB);
}
}
Output:
2018-12-19 is later than 2017-02-01
2019-09-12 is later than 2014-03-01
Link: Oracle tutorial: Date Time explaining how to use java.time.

Related

How can I tell if DateTime.Now() is on a day AFTER a different DateTime

I'm running this on flutter, but I guess this could be a more general issue.
I am saving a DateTime in the preferences. I want to be able to then tell if DateTime.now() is on at least a day after the last saved DateTime, i.e.
(pseudocode)
lastDailyCheck = 2020.04.10
now = 2020.04.11
=> now is a day after the lastDailyCheck.
This should already work if it is 00:01 on the new day, even if the lastDailyCheck was on 23:58 the day before, meaning the difference can be as low as minutes between the 2 DateTimes.
Turns out this is really complicated!
Here's what doesn't work:
DateTime.Now().isAfter(lastDailyCheck)
This only checks if now is after the last one, it also return true after a second, and on the same day.
DateTime.Now().isAfter(lastDailyCheck) && lastDailyCheck.day != DateTime.Now().day
I thought this was clever. If the day is different and it is after the last then it does work in recognizing it is a day later - but then I realized it would bug out when both days are say on the 15th of the month - then lastDailyCheck.day would equal DateTime.Now().day.
What do you think would be possible here?
I don't know flutter, but my approach would be to not store the last check, but store the date at which the next check should occur. So when you perform a check you calculate the next midnight and store that. Now you can use isAfter.
In javascript this would look something like this:
const now = new Date();
//this also handles overflow into the next month
const nextCheck = new Date(now.getYear(), now.getMonth(), now.getDate() + 1)
//store nextCheck somewhere
//in js there is no isAfter, you just use >
if(new Date() > nextCheck) {
//do the thing
}
of course you could also calculate nextCheck every time you want to compare it, but I dislike performing the same calculation over and over if I can avoid it.
A thing to mention here is timezones, depending on your date library and if your system and user timezones align, you may need to shift the date.
I cannot write a complete code for now but this is what it would look like:
(pseudocode)
expirationDay = lastDailyCheck.add(oneDayDuration);
isOneDayAfter = DateTime.now().isAfter(expirationDay);
You give an expiration date and compare the DateTime to that. You have to use isAfter for reliability, instead of .day check.
I would compute the difference between midnight of the day of the last timestamp and midnight of the current timestamp. That is, consider only the date portion of a DateTime and ignore the time.
DateTime date(DateTime dateTime) =>
DateTime(dateTime.year, dateTime.month, dateTime.day);
// Intentionally check for a positive difference in hours instead of days
// so we don't need to worry about 23-hour days from DST. Any non-zero
// number of hours here means a difference of at least a "day".
if (date(DateTime.now()).difference(date(lastDailyCheck)).inHours > 0) {
// "One day" after.
}
If you're using UTC timestamps and don't care about when midnight is in whatever the local time is, the comparison could more intuitively use .inDays >= 1.
Figured out another potential solution!
In addition to checking if the day is different (which by itself won't work) you can also check the month and year. Only 1 of those needs to differ for it be true :)
if (now.isAfter(lastDailyCheck)) {
if (now.day != lastDailyCheck.day ||
now.month != lastDailyCheck.month ||
now.year != lastDailyCheck.year) {
return true;
}
}
this is the way I prefer to do some logics based on the comparison between two different times:
var now = DateTime.now();
var myDate = new DateTime.utc(2022, 1, 1);
if(myDate.compareTo(now)>0) //positive value means myDate is greater than DateTime.now()
{
// here is your logic based on the comparison between two times
} else {
//your logic if DateTime.now() pass myDate
}
You can use the difference method to get the difference between 2 dates and check whether those differs in hours with at-least 24 hours. So your if condition becomes:
if (now.isAfter(lastDailyCheck) &&
(lastDailyCheck.day != now.day ||
now.difference(lastDailyCheck).inHours > 24)) {
print('After');
}
Here's an attempt to a succint answer. Simply export this extension and use it. With it you can say if a date is at least one day after the current day.
extension DateExt on DateTime {
bool isAtLeastOneDayAfterToday() {
final now = DateTime.now();
return (isAfter(now) &&
(day != now.day || month != now.month || year != now.year));
}
}
Use it like so:
final isAfter = myDay.isAtLeastOneDayAfterToday(); //will be true or false

Formatting a Time value on Ionic 3

I need to find the way to format a time, I tried with angular pipe, but this works with date type values.
I need to be able to remove the seconds to values of the hours shown, example:
1:45:00 change to 1:45 pm or 1:45 p.m. M.
Assuming your date is a instance of Date you can use the built in angular date pipe with the predefined format shortTime or a custom format:
<p> {{date | date:'shortTime'}} </p>
<p> {{date | date:'hh:mm'}} </p>
shortTime is equivalent to 'h:mm a' and will produce results like 9:03 AM.
The custom format 'hh:mm' will produce results like 09:03.
If your date is just a string you could use the built in slice pipe to remove the parts you want to get rid of:
<p> {{"1:45:00" | slice:0:4}} </p>
Which will output 1:45.
Also see this Stackblitz for the different options.
Anyway I'd reccomend using real Date objects or Moment.js objects over bare strings, it makes things a lot easier, especially once you start comparing dates or calculating with dates.
Use Moment.js .here you can convert to any time format
1 - Install via NPM:
npm install moment -S
2 - Import in your Typescript file:
import moment from 'moment';
3 - Use in your Typescript file:
let dateString = "22-04-2017"; //whatever date string u have
let dateObject = moment(dateString, "DD-MM-YYYY").toDate();
If this is format is always the case you can manipulate the string with JavaScript/TypeScript.
myTime = '1:45:00'
showTime() {
var result =
this.myTime.substring(
0, (this.myTime.length - 3) )+ ' pm';
console.log(result);
}
If you have more complicated cases You could use a library like Momentjs.
https://momentjs.com/
This is where you can find what you need in the docs.
https://momentjs.com/docs/#/parsing/string-format/
you can use a custom pipe
transform(timeString: string) {
let time = timeString.slice(0, 5);
let current_hour = timeString.slice(0, 2);
if (parseInt(current_hour) > 12) {
time = time + " PM";
} else {
time = time + " AM";
}
return time;
}

CakePHP 3 - Comparing and modifying dates

In the CakePHP 3 Cookbook on Date/Time, you can compare time intervals with future/past days/weeks using IsWithinNext/WasWithinNext. You can also modify dates/times by doing a ->modify('extra time') - eg. if $date = 2016-01-01, $date->modify('+1 week') would mean $date = 2016-01-08.
These features require the use of Cake\i18n\Time. However, when I attempted to use these features, I received a Cake error:
Call to a member function isWithinNext() on string.
This is the code I used:
$date_start = \Cake\Database\Type::build('date')->marshal($data['session']['date_start'])->i18nFormat(); //before hand my dates were in the form of an array comprised of Year, Month and Day. This changes them into date format.
if($date_start->isWithinNext('1 week')){
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+48 hours');
} elseif ($date_start->isWithinNext('2 weeks')){
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+1 week');
} elseif ($date_start->isWithinNext('3 weeks')){
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+1 week');
} else {
$deposit_due = $booking->date_confirm;
$deposit_due->modify('+2 weeks');
}
Calling i18nFormat() returns a formatted string as you can look up in the API: https://api.cakephp.org/3.4/class-Cake.I18n.DateFormatTrait.html#_i18nFormat
This, for example, should work:
$date_start = new \Cake\I18n\Time($data['session']['date_start']);
debug($date_start->isWithinNext('2 weeks'));

Parsing Date in Java using SimpleDateFormat

Developing an Application where i have to parse the following date:
2015-02-02T11:21:51.895Z
using SimpleDateFormat class. But I am getting a Date Parsing Exception.
Here is my code snippet:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd'T'hh:mm:ss'Z'");`
Date qdate = new GregorianCalendar(0,0,0).getTime();
try {
qdate = sdf.parse(dt);
} catch (ParseException e) {
}
Regarding your input "2015-02-02T11:21:51.895Z" you should see that your assumed pattern does not match the input because the pattern does not expect the millisecond part but the literal "Z".
Beyond this, the pattern you used is wrong because of following reasons:
m = minute
M = month
h = hour of half day (1-12)
H = hour of full day (0-23)
X = timezone designator (because Z is not a literal but stands for UTC+00:00)
So you need (please also refer to javadoc):
yyyy-MM-dd'T'HH:mm:ss.SSSX
There is an error in your format (add .SSS for the miliseconds):
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd'T'hh:mm:ss.SSS'Z'");
Date date = sdf.parse("2015-02-02T11:21:51.895Z");

What is the most efficient way to convert an eight digit number to a date?

I am using ColdFusion 9.0.1 and some database that I cannot change.
I am accessing a database that stores a date as an eight digit numeric with zero decimal places like this:
YYYYMMDD
I need to be able to read the date, add and subtract days from a date, and create new dates. I am looking for a ColdFusion solution to efficiently (not much code) to convert the date to our standard format, which is
MM/DD/YYYY
And then convert it back into the database's format for saving.
I need to code this in such a way that non-ColdFusion programmers can easily read this and use it, copy and modify it for other functions (such as adding a day to a date). So, I am not looking for the most least amount of code, but efficient and readable code.
Can you suggest anything that would make this code block more flexible, readable, or more efficient (less code)?
<cfscript>
// FORMAT DB DATE FOR BROWSER
DateFromDB = "20111116";
DatedToBrowser = createBrowserDate(DateFromDB);
writeOutput(DatedToBrowser);
function createBrowserDate(ThisDate) {
ThisYear = left(ThisDate, 4);
ThisMonth = mid(ThisDate, 4, 2);
ThisDay = right(ThisDate, 2);
NewDate = createDate(ThisYear, ThisMonth, ThisDay);
NewDate = dateFormat(NewDate, "MM/DD/YYYY");
return NewDate;
}
// FORMAT BROWSER DATE FOR DB
DateFromBrowser = "11/16/2011";
DateToDB = createDBDate(DateFromBrowser);
writeDump(DateToDB);
function createDBDate(ThisDate) {
ThisYear = year(ThisDate);
ThisMonth = month(ThisDate);
ThisDay = day(ThisDate);
NewDate = "#ThisYear##ThisMonth##ThisDay#";
return NewDate;
}
</cfscript>
First find who ever did the database and kick them in the nads...
Personally I'd Convert with sql so my code only dealt with date objects.
Select Convert(DateTime, Convert(VarChar(8),DateTimeInventedByIdjitColumn))
From SomeTable
As stated by our peers, store dates as dates.
'08/06/2011' could be 8th of june of the 6th of August depending on locale.
20111643 is a valid integer..
Not using a proper date type is just a massive collection of features and bugs that at best are waiting to happen.
You can actually rewrite each function into 1 line of code.
function createBrowserDate(ThisDate) {
return mid(ThisDate,4,2) & "/" & right(ThisDate,2) & "/" & left(ThisDate,4);
}
and
function createDBDate(ThisDate) {
return dateFormat( ThisDate, "YYYYMMDD" );
}
Don't keep dates as strings - keep dates as dates and format them when you need to.
If you can't correct the database to use actual date columns (which you should if you can), then you can use these two functions to convert to/from YYYYMMDD and a date object:
function parseYMD( YYYYMMDD )
{
if ( ! refind('^\d{8}$' , Arguments.YYYYMMDD ) )
throw "Invalid Format. Expected YYYYMMDD";
return parseDateTime
( Arguments.YYYYMMDD.replaceAll('(?<=^\d{4})|(?=\d{2}$)','-') );
}
function formatYMD( DateObj )
{
return DateFormat( DateObj , 'yyyymmdd' );
}
By using date objects it means that any level of developer can work with them, without needing to care about formatting, via built-in functions like DateAdd, DateCompare, and so on.
I'm not a regular expression fan since it's not that readable to me.
Since you're using CF9, I'd typed the argument and specify the returntype of the functions to be even more readable for the next person picking up your code.
First, right after I read the date from DB, I'd parse it to a Date object using parseDBDate()
Date function parseDBDate(required String dbDate)
{
var yyyy = left(dbDate, 4);
var mm = mid(dbDate, 4, 2);
var dd = right(dbDate, 2);
return createDate(yyyy , mm, dd);
}
Once you have the date object, you can use all those built-in Date functoin like DateAdd() or DateDiff().
Call browserDateFormat() right before you need to display it.
String function browserDateFormat(required Date date)
{
return dateFormat(date, "MM/DD/YYYY");
}
Call dBDateFormat() inside <cfqueryparam value=""> when it's time to persist to DB
String function dBDateFormat(required Date date)
{
return dateFormat(date, "YYYYMMDD");
}
One liner :)
myDateString = "20110203";
myCfDate = createObject("java","java.text.SimpleDateFormat").init("yyyyMMdd").parse(myDateString,createObject("java","java.text.ParsePosition").init(0*0));
If you want to parse different patterns, change "yyyyMMdd" to any other supported pattern.
http://download.oracle.com/javase/1.5.0/docs/api/java/text/SimpleDateFormat.html
The ParsePosition is used to say where to start parsing the string.
0*0 is shorthand for JavaCast("int",0) - in the Adobe cf engine, 0 is a string, until you apply math to it, then it becomes a Double, which the ParsePosition constructor supports. Technically, it constructs with an int, but cf is smart enough to downgrade a Double to an int.
http://download.oracle.com/javase/1.5.0/docs/api/java/text/ParsePosition.html