Changing the Session Languge leads to "java.text.ParseException: Unparseable date - datetime

whenever I'm defining the timeframe being in German session language after changing to English lang. session (and vice versa) I'm getting the:
java.text.ParseException: Unparseable date: "10.10.2018"
Here is the fragment:
Date startDateFormatted = DateUtils.convertDateToMinusDayNumber(cal, dayRange);
Date endDateFormatted = new Date();
if (StringUtils.isNotEmpty(startDate) && StringUtils.isNotEmpty(endDate))
{
try
{
String datePattern = getLocalizedString("dd.MM.yyyy"); //
startDateFormatted = new SimpleDateFormat(datePattern).parse(startDate); // exception is throwing on this line
endDateFormatted = new SimpleDateFormat(datePattern).parse(endDate);
}
catch (final Exception e)
{
LOG.error(ERROR_DATE_PARSING, e);
}
}

java.time
I recommend you use java.time, the modern Java date and time API, for your date work.
String datePattern = "dd.MM.uuuu";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(datePattern);
String startDateString = "10.10.2018";
LocalDate startDate = LocalDate.parse(startDateString, dateFormatter);
System.out.println(startDate);
Output:
2018-10-10
If you want to support different date formats for different locales, let Java handle that part for you:
String datePattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.GERMAN);
German locale works with your example string of 10.10.2018. For UK locale, for example, a string like 10 Oct 2018 would be required instead, as Britons would typically expect.
What went wrong in your code?
We cannot tell from the information and code that you have provided exactly what happened. A couple of good guesses are:
As Arvind Kumar Avinash said in a comment, getLocalizedString() may be causing trouble. You may print datePattern to check. Localization is something you do to strings that you display to the user. Trying to localize a format pattern string for a formatter is probably plain wrong, so you should leave out that method call. That the error occurs when changing language seems to support this possibility.
There may be unexpected non-printing characters in your string. One way to check would be to print startDate.length(). If the length is greater than 10, there are more characters than the 10 chars in 10.10.2018.
Link
Oracle tutorial: Date Time explaining how to use java.time.

Related

Get time format according to spreadsheet locale?

I want to store a Javascript Date() object in a spreadsheet with correct format according to spreadsheet's locale (SpreadsheetApp.getActive().getSpreadsheetLocale()).
Is there a way to get the country specific (date and) time format string from the spreadsheet locale?
E.g. when locale is de_DE, time format string as hh:mm
but when locale is da_DK, time format string as hh.mm
Interesting as well how to get the countries currency format.
BTW when I have date and time in de_DE and than change to da_DK, dates are reformatted (23.01.2020 -> 23/01/2020) but times are not (it stays as 22:59). Is that an error in Spreadsheet?
Dates in JavaScript have the method toLocaleDateString, which return a string formatted according to the specified locale. But this doesn't seem to work in Apps Script.
If you're open to using an Apps Script Web App for this, you could use this toLocaleDateString in your client-side script (that is, in a script tag in your HTML).
If that's not the case, I think your best option would be to create the relationship between formats and locales yourself, because Apps Script doesn't have a built-in method to achieve that. You could, for example, use a switch statement that would check the locale, and then format the date accordingly with Utilities.formatDate, the tool Apps Script uses to format dates. It could be something along the following lines:
var locale = SpreadsheetApp.getActive().getSpreadsheetLocale();
var formattedDate;
switch (locale) {
case 'de_DE':
formattedDate = Utilities.formatDate(yourDate, yourTimeZone, "hh:mm");
break;
case 'da_DK':
formattedDate = Utilities.formatDate(yourDate, yourTimeZone, "hh.mm");
break;
// ...
}
return formattedDate;
Reference:
toLocateDateString
Apps Script Web Apps
Utilities.formatDate
I hope this is of any help.
Sorry for that, however I found a function that would be worth checking out, it's toLocaleDateString() and toLocaleTimeString (), they deliver the local date and time format.
Please check
Formato fechas JavaScript.
I did the test from Google Apps Script and it throws me the following
function pruebafecha() {
var d = new Date();
var n = d.toLocaleDateString();
var h = d.toLocaleTimeString();
Logger.log(n);
Logger.log(h);
}
This is the answer(Colombia):
[20-01-24 16:47:50:286 EST] 24 de enero de 2020
[20-01-24 16:47:50:287 EST] 16:47:50 EST
A JavaScript Date object includes date, time and timezone. When Google Apps Script pass a Date object to the spreadsheet using setValue() / setValues() the value is displayed according to the cell number formatting using the spreadsheet timezone.
If the cell formatting is set to Automatic by default the date will be displayed accordingly to the spreadsheet locale.
If you want to force the cell to display a date in an specific format use Class Range setNumberFormat / setNumberFormats
If you don't want to use the above methods and don't want to rely on the spreadsheet locale and automatic cell format then instead of passing a Date object pass the value as an string prepending it with an ' (apostrophe, single quote character) to prevent that that automatic data type parsing changes the value and it's format.
Related
Javascript in Google Sheets script: help using setNumberFormat
I don't know very well the configuration of the sheet you mention. However, I share a code that I use to print the date and time of data submission of a form.
var d = new Date();
var hour = d.getHours()-1;
var min = d.getMinutes();
var day = d.getDate();
var month = d.getMonth()+1;
var year = d.getFullYear();
if (month<10) {dia = day+"/"+"0"+month+"/"+year;}
else {dia = day+"/"+month+"/"+year;}
if (min<10){time = hour+":"+"0"+min;}
else {time = hour+":"+min;}
What I do in the code is to take the values โ€‹โ€‹of day, month and year, I add 1 to the value of month because it takes values โ€‹โ€‹[0:11] => [Jan, Dec].
Then I build the format I want from date and time, you can notice that I have 1 left to the hours, because when I did the tests I noticed that the time of the script was one hour above.
I use google translate, I hope it is understood.

moment.js will not parse UK format date even when setting the locale

Quite simply, this is my code:
http://jsfiddle.net/NibblyPig/k9zb4ysp/
moment.locale('en-GB');
var d = moment('22/12/2019');
alert(d);
I would expect this to parse, however it says invalid date.
I have referenced moment.js and the locale/en-gb.js
I'm writing a global control so the date may come in in a variety of formats.
If I put in a variety of American dates they all work, for example 12/12/2019, 12/12/2019 23:04 etc.
However the locale command does not appear to do anything and I cannot get a single date to parse. What am I doing wrong?
You need to pass the format as the second argument for moment(), as discussed here:
moment.locale('en-GB');
var d = moment('22/12/2019', 'DD/MM/YYYY');
alert(d);
https://jsfiddle.net/a4gu6kfz/
From the docs:
If you know the format of an input string, you can use that to parse a
moment.
moment("12-25-1995", "MM-DD-YYYY");
I think that there is no need to write your own complex logic to parse your input, you can use moment(String, String) (or moment(String, String[], String, Boolean)), as suggested by Thales Minussi's answer.
moment(String) is the good choice only if your input is in ISO 8601 or RFC 2822 compliant form.
In your case, you can probably use Localized formats listed in the format section of the docs. If you have a list of possible formats, I think that the best choice is tho use moment(String, String[]).
Please note that, by default: Moment's parser is very forgiving, so using default Forgiving Mode will handle "any" character as separator.
Here a live sample:
moment.locale('en-GB');
['22/12/2019', '22/12/2019 15:00',
'22-12-2019', '22-12-2019 15:00',
'1-3-2019', '1-12-2019', '22-1-2019'
].forEach((elem) => {
var d = moment(elem, 'L LT');
console.log(d.format());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/locale/en-gb.js"></script>
Still hoping there's a nice moment js way to do this but in the meantime I just bashed this together. Pretty nasty and it will probably go wrong in 80 years or so.
http://jsfiddle.net/NibblyPig/k9zb4ysp/22/
var a = "23/03/19 12:42:21.123";
var datePart = a.substring(0, a.indexOf(" "));
var timePart = a.substring(a.indexOf(" ") + 1);
var dateParts = datePart.split("/");
if (dateParts[0].length == 1) dateParts[0] = "0" + dateParts[0];
if (dateParts[1].length == 1) dateParts[1] = "0" + dateParts[1];
if (dateParts[2].length == 2) {
var threshold = parseInt(new Date().getFullYear().toString().substring(2)) + 10;
if (parseFloat(dateParts[2]) > threshold ) {
dateParts[2] = "19" + dateParts[2];
}
else
{
dateParts[2] = "20" + dateParts[2];
}
}
alert (parseFloat(dateParts[2] + dateParts[1] + dateParts[0] + timePart.replace(/:/g, "").replace(/\./g, "")));
This won't solve every usecase, but in your specific example if you want just a simple date (with no time component) auto-parsed in UK format you can just use the 'L' format string having set the locale to 'en-GB'
Your example with this change (your jsfiddle also)
moment.locale('en-GB');
// just pass 'L' i.e. local date format as a parsing format here
var d = moment('22/12/2019', 'L');
alert(d);
It's quite nice because you get the auto parsing of various formats you wanted for free. For instance this works just the same:
var d = moment('22-12-2019', 'L');
You can return a date using moment.js in a desired format -
return moment(aDateVar).format('DD/MM/YYYY');

Using str2date with strings that also contain a time

I have a method which, given an .NET XmlNode containing an ISO 8601 date in the inner text, will convert it to an X++ date object.
if (CLRInterop::isInitialized(childNode))
{
return str2Date(childNode.innerText(), 321);
}
else return maxDate();
This works great if supplied a string which contains only a date (eg: 2019-03-21), but as soon as a time is also provided in this string (eg: 2019-03-21T00:00:00), it will return nothing.
The easiest fix for this would be just to strip everything past the first 10 characters, but this would break again if for some reason the string only contains 2 characters for the year. Is there a more robust way of handling strings including times in a call to str2date?
I just wrote this job with a bunch of examples. The very first line might be what you want. You can just create this as a new job in AX and then put a breakpoint on the first line and step through each to see what happens, or modify to experiment.
It looks like your string is standard ISO format, which I cover below various ways too.
static void DateTimeJob(Args _args)
{
// This line looks about what you want
utcDateTime utcDateTimeFromString = DateTimeUtil::anyToDateTime("2019-03-21T00:00:00");
// ISO standard format. You can just assign it directly without quotes
utcDateTime utcDateTimeISOFormat = 2019-03-21T00:00:00;
// Misc vars for below
utcDateTime utcNow;
System.DateTime systemDateTime;
date dateOnly;
str systemDateTimeStr;
// Look at
// DateTimeUtil::<> // This has all sorts of useful functions
// str2datetime() // May be useful to you
try
{
// How to go from AX UTC to System.DateTime
systemDateTime = Global::utcDateTime2SystemDateTime(DateTimeUtil::utcNow());
// How to go from System.DateTime to AX UTC
utcNow = Global::clrSystemDateTime2UtcDateTime(System.DateTime::get_UtcNow());
// How to get ONLY the date portion from a UTC
dateOnly = DateTimeUtil::date(utcNow);
// Cast to string for output
systemDateTimeStr = systemDateTime.ToString();
// Output a few examples
info(strFmt("%1, %2, %3",
systemDateTimeStr,
utcNow,
dateOnly));
}
catch (Exception::CLRError)
{
error(AifUtil::getClrErrorMessage());
}
}

ZonedDateTime: I would like parse() to fail on invalid date

I guess I just expected this to give me a parsing exception, but it didn't. I would like the parse to fail if the date is invalid. (That's what I need to know)
Added note: Using Feb. 30th as an invalid date: (In fact, what I need to do is receive five strings; year, month, day, hour, minute, which I assembly into str (below) and see if they come together as a valid date-time (in ZONE_ID).
String ZONE_NAME = "America/Los_Angeles";
ZoneId ZONE_ID = ZoneId.of(ZONE_NAME);
String ldtfPattern = "yyyy/MM/dd HH:mm";
DateTimeFormatter localDateTimeFormatter =
DateTimeFormatter.ofPattern(ldtfPattern);
String str = "2016/02/30 21:09";
try {
zdt = ZonedDateTime.parse(str, localDateTimeFormatter().withZone(ZONE_ID));
} catch(DateTimeParseException e) {
return null;
}
return zdt;
The string value of zdt after the parse above is
2016-02-29T21:09-08:00[America/Los_Angeles]
A little birdie told me to try using withResolverStyle() set to STRICT. With that, the only other trick I needed to make this work was to incorporate the era into my year. So the changes to the above are:
String ldtfPattern = "uuuu/MM/dd HH:mm";
and
zdt = ZonedDateTime.parse(str,
getLocalDateTimeFormatter().withZone(ZONE_ID).withResolverStyle(ResolverStyle.STRICT));
And this is working for me.

Date field changing from UK to US culture upon SaveChanges (EF6.1)

I am using a custom overloaded SaveChanges to implement some auditing functionality. The functionality works perfectly with the exception of some unexpected behaviour in relation to dates. In this example I'm changing a date field value from 1st May 2014 to 2nd May 2014:
A change is made to the database here:
Public Function UpdateTask(request As DataSourceRequest, ThisTask As JobTasksVM) As JsonResult
Dim cu As ApplicationUser = GetCurrentUser()
Dim CurrentTask = db.events.Find(ThisTask.id)
CurrentTask.start_date = ThisTask.start '<--- 2/5/2014 (ie 2nd May 2014), was previously 1/5/2014
CurrentTask.date = ThisTask.end
CurrentTask.task_name = ThisTask.Title
db.SaveChanges(cu.Employee_id)
End Function
This is intercepted by my custom SaveChanges:
Public Overloads Function SaveChanges(userID As Integer) As Integer
For Each entry As dbentityentry In Me.ChangeTracker.Entries().Where(Function(e) e.State <> EntityState.Unchanged)
Dim startOriginal As DateTime = entry.OriginalValues.Item("start_date")
Dim StartCurrent As DateTime = entry.CurrentValues.Item("start_date")
etc....
The bizarre thing is that whilst CurrentTask.start_date that is committed clearly shows the correct (UK) date of 2/5/2014 (2nd May 2014) the values within the overloaded SaveChanges are:
startOriginal: 5/1/2014 (ie 5th Jan 2014) <-- seems to have changed to US culture
startCurrent: 2/5/2014 (ie 2nd May 2014) <---as expected
I need to use the Original values in my audit functionality so this is causing a problem. I have also tried:
entry.CurrentValues.SetValues(entry.GetDatabaseValues)
But this also loads in the erroneous (ie US format 5/1/2014) into the start_date field.
I've checked all the culture settings on the system and they are all correctly English-UK. This behaviour seems fundamentally inconsistent - am I missing something?!
Thanks
DateTime types do not have a format, they are simply a value (number of ticks since 1/1/0001).
You did not say where you are seeing the "wrong" format, whether ToString() output or in intellisense. If you use ToString to the Output window, you should see the UK format since ToString will use/respect the local culture setting of the computer.
Intellisense is culture agnostic and tries to use an unambiguous format: MM/dd/yyyy. This is the same "format" or order you have to use when creating a DateTime var from a literal:
Dim dt As DateTime = #1/5/2014# ' e.g. MM/dd/yyyy
' same as:
Dim dt As New DateTime(1, 5, 2014) ' mm, dd, yyyy
This is InvariantCulture (not US). When you hold the mouse over the var, the VB IDE will use the same order. It tries to make clear it is using the required literal/InvariantCulture format by displaying it with the hashes: #1/5/2014#.
Dim dt As DateTime = #2/11/2011#
Console.WriteLine(dt.ToString)
In the US, 2/11/2011 will display based on the culture
In the UK, it will be 11/2/2011
Intellisense will be #2/11/2011#

Resources