What is the most efficient way to convert an eight digit number to a date? - 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

Related

Comparing date by combining it

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.

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'));

Converting time in SPSS from hhmm:ss to hh:mm:ss (TIME8)

After a data export I get a string variable with "2017/02/22 1320:35 +000 4".
Through:
compute #TS = char.index(Timestamp_1, " ").
string date (A10).
compute date = char.substr(Timestamp_1,1,#TS).
alter type date (A10 = SDATE10).
I manage to get the date in a separate variable.
The same:
string time (A8).
compute time = char.substr(Timestamp_2,#TS+1,7).
alter type time (A8 = TIME8).
doesn't work for the time because it is in the 'hhmm:ss' format. How do I change the string variable '1320:35' into a time variable '13:20:35'?
You can insert a ":" manually into the time string by using the concat function before you alter the type.
COMPUTE time = CONCAT(CHAR.SUBSTR(time,1,2),":",(CHAR.SUBSTR(time,3))).
Another approach would be to extract hours, minutes and seconds from Timestamp variable individually and to use these elements inside the TIME.HMS function:
COMPUTE #hh = NUMBER(CHAR.SUBSTR(Timestamp_1,TS+1,2),F2).
COMPUTE #mm = NUMBER(CHAR.SUBSTR(Timestamp_1,TS+3,2),F2).
COMPUTE #ss = NUMBER(CHAR.SUBSTR(Timestamp_1,TS+6,2),F2).
COMPUTE time = TIME.HMS(#hh,#mm,#ss).
EXECUTE.
FORMATS time (TIME8).
Statistics has a datetime format and, new in V24, an ISO 8601 timestamp format, YMDHMS. If the string is converted to a regular date/time value, then the XDATE.DATE and XDATE.TIME functions can be used to extract the pieces. The Date and Time wizard may accommodate the format you have (not sure about that +000 4 part at the end).

Get the whole range of date in SAS

I know the date in SAS looks like 01Jan2017. What I want is 1 January 2017. Is there function to make it?
Thank,
Andrea
Your question is pretty vague - it's hard to tell if you just want to display it differently or store it differently.
Display it differently: Just change the format to keep the date as a number in the background (number of days since 01JAN1960) but display it how you would like.
data ds1;
date = '01JAN2017'd;
format date WORDDATX20.;
run;
Store it differently: You can use the put function to create a separate character variable containing the formatted version of your date.
data ds2;
date1 = '01JAN2017'd;
date2 = put(date1, WORDDATX20.);
run;
Answers provided by Bhavika and pm2r should also help you understand what's going on here.
your code would look like this:
data _null_;
length date1 8. string $40;
date1=today();
string = cats( date1 );
put string=;
string = cats( put(date1,date10.) );
put string=;
string = cats( put(date1,WORDDATX20.) );
put string=;
run;
and the output would be:
string=20838
string=19JAN2017
string=19 January 2017

Groovy date format for UTC with milliseconds

I'm having trouble finding a good way of formatting a UTC-time stamp with this format: yyyyMMdd-HH:mm:ss.<three additional digits>
I wasn't able to find any character that represents milliseconds/hundredths, I'm not even sure this is possible, to parse that format that is.
Ideally I'd like to use the parseToStringDate that's part of the Date library.
My plan b is to convert yyyyMMdd-HH:mm:ss to milliseconds and then add the three last digits to that number.
Use yyyyMMdd-HH:mm:ss.SSS
This will get you milliseconds as well.
Test Code:
def now = new Date()
println now.format("yyyyMMdd-HH:mm:ss.SSS", TimeZone.getTimeZone('UTC'))
I would convert it like that:
def now = new Date()
println now.format("YYYYMMdd-HH:mm:ss")
You can try this:
TimeZone.getTimeZone('UTC')
Date date = new Date()
String newdate = date.format("YYYY-MM-dd HH:mm:ss.Ms")
log.info newdate