One of my co-workers has written an application in Grails 2.4.4 (I know, it's dated). One problem the app has is that you can enter a date like 2/31/2015 and it will be accepted as valid and will show up in your domain object as 3/3/2015 instead.
Is there any easy way to prevent this from happening using grails? Or do we instead have to rely on client side validation for this particular property?
Thanks.
Assuming that Grails is using DateFormat to parse the date String, the issue is that it's using a lenient Calendar. For example, with lenient set to true (the default), the result is as you described:
import java.text.SimpleDateFormat
def sdf = new SimpleDateFormat('MM/dd/y')
assert sdf.parse('02/31/2015').toString() == 'Tue Mar 03 00:00:00 EST 2015'
But, if you change it to false, you'll get an exception for the same date:
sdf.lenient = false
try {
sdf.parse('02/31/2015').toString() == 'Tue Mar 03 00:00:00 EST 2015'
} catch (java.text.ParseException ex) {
// java.text.ParseException: Unparseable date: "02/31/2015"
println 'Oops'
}
So to handle this validation in your controller, you can
Create a command object.
Have the command object accept the date as a String rather than a Date, and perform the date validation.
Modify the controller action to use the command object instead of params. This may require modifying the GSP code as well.
Related
I have a rails/react app (just one app) in which a user is allowed to schedule a meeting using Flatpickr .
I am passing down a datetime column called "scheduled_for" so that I can use it in my react component.
This is what my "componentDidMount()" looks like:
componentDidMount = () => {
new Flatpickr(this.refs.scheduledFor, {
minDate: new Date(),
enableTime: true,
altInput: true,
altFormat: "F j, Y h:i K",
onChange: function(dateObject) { console.log(dateObject) }
});
}
There is a "scheduled_for_future" validation method in my Meeting model to prevent the meeting from being scheduled in the past.
##app/models/meeting.rb
validate :scheduled_for_future
def scheduled_for_future
if scheduled_for.present? && scheduled_for < Time.zone.now
errors.add(:scheduled_for, "Must be in future")
end
end
I want a user to be able to pick a date & time in their local time zone and have it be saved as UTC (the Heroku default).
Everything works fine on local dev but if I try to pick a time & date in production, say for example 10 minutes from now, I get the "Must be in the future" error. (this obviously occurs because my Timezone is PT and 10 minutes from now is in the past according to the server's time)
It feels like this should be simple to fix. For the sake of UX I want the client to be able to pick the time in their own time zone and have convert to UTC before saving, but just can't figure it out.
I'm not very experienced with momentjs or flatpickr so it's likely that I'm missing something very important.
Please let me know if you need any more info/ something doesn't make sense.
thanks a million
You can use moment to format the datetime on the client side to include the timezone offset. currDate, in your case would be the datetime selected in your Flatpickr calendar.
var currDate = new Date();
console.log("Current Date: " + moment(currDate).format("YYYY-MM-DD HH:mm:ssZ"));
// Returns ...
Current Date: 2017-02-25 09:38:02-05:00
Then you can pass that to up rails, as a string, and convert it to UTC before persisting in the database
2.3.1 :003 > client_date = "2017-02-25 09:38:02-05:00"
=> "2017-02-25 09:38:02-05:00"
2.3.1 :004 > utc_date = Time.zone.parse(client_date).utc
=> 2017-02-25 14:38:02 UTC
I have a Grails 2.2.4 project, and I'm trying to write a unit test for a method that queries over lastUpdated, like so:
Tile.createCriteria().list {
lt('lastUpdated', new Date() - 1)
}
This method works fine in real life, but fails in my unit tests because I can't create any test data with lastUpdated other than now. Setting myTile.lastUpdated explicitly doesn't work, since that's an update and thus triggers the auto-timestamping. Turning off auto timestamping requires the eventTriggeringInterceptor, which doesn't seem to be available in unit tests. Mocking the default Date constructor to return other values was also no help. Direct SQL updates are not available in unit tests at all.
Is this possible in unit tests at all, or do I have to write an integration test?
It's interesting that you say mocking the default date constructor to return other values is no help. I successfully do that quite often when I have queries such as yours that new up a date.
For your situation, I would have a unit test that looked something like this:
def 'test lastUpdated query'() {
setup:
Title lessThan = new Title(lastUpdated:new Date(1477152000000)) //22 Oct 2016 16:00 UTC, should be found
Title equalTo = new Title(lastUpdated:new Date(1477238400000)) //24 Oct 2016 16:00 UTC, should not find, not less than 1 day before, but equal to 1 day before
Title notLessThan = new Title(lastUpdated:new Date(1477296000000)) //24 Oct 2016 08:00 UTC, should not find, not less than 1 day before
Date date = new Date(1477324800000) //24 Oct 2016 16:00 UTC
Date.metaClass.constructor = {-> return date}
when:
List<Title> result = service.someMethod()
then:
result.size() == 1
result.contains(lessThan)
!result.contains(equalTo)
!result.contains(notLessThan)
}
I have a problem within a grails 2.3 application, when it comes to data binding and correct date formats.
I use a datepicker (jQuery ui) that provides a <input type="hidden" /> that holds the selected date in ISO_8601 format. It will post a value like this: 2015-08-14 to the controller. The form itself and the post result is correct.
I use this simplified model:
class Thing {
DateTime lastUpdated
static constraints = {
lastUpdated nullable: true
}
}
when I try to create an entity, I will face this error message:
Invalid format: "2015-08-14" is malformed at "15-08-14"
A first research lead me to this change in the Config.groovy:
jodatime.format.html5 = true
(Link 3 in the list below)
Appying this leads to change. Now the error message is:
Invalid format: "2015-08-14" is too short (flip table)
Another try was to change the databinding.dateFormats to this (also in the Config.groovy):
grails.databinding.dateFormats = [ "yyyy-MM-dd HH:mm:ss.S","yyyy-MM-dd'T'hh:mm:ss'Z'", "yyyy-MM-dd"]
Which has no effect what so ever.
For my understanding a given date format should automatically be marshaled in a dateTime object. What configuration did I miss?
Here are relative question, that sadly did not help me:
bind date to command object in Grails
GORM default Date format when sending date to Grails
Grails unable to unmarshall date/time from JSON back into joda DateTime
You should add next line in config.groovy
jodatime { format.org.joda.time.DateTime = "yyyy-MM-dd" }
But if you don't need time in this field, it's better to use LocalDate instead of DateTime here.
class Thing {
LocalDate lastUpdated;
...
jodatime {
format.org.joda.time.DateTime = "yyyy-MM-dd HH:mm:ss"
format.org.joda.time.LocalDate = "yyyy-MM-dd"
}
So you will use DateTime where you need date with time and LocalDate where date is enough
I have a form on which I set a start Date and a finish Date for a entity.
On the Web Api side, before saving the date to the database,I set the start date: 2013-09-25 00:00:00.000 and the the end date as 2013-09-26 23:59:59.000.
var vote = (VotingSet)Entity;
vote.Start = new DateTime(vote.Start.Year, vote.Start.Month, vote.Start.Day, 0, 0, 0, 0);
vote.End = new DateTime(vote.End.Year, vote.End.Month, vote.End.Day, 23, 59, 58);
This is from the JSON that is send to the rest service looks like this:
Start: "2013-09-25T00:00:00.000Z"
End: "2013-09-26T00:00:00.000Z"
After the save, in the javascript client, the entity is updated with the new key and with the properties that come from the server.
The observable date objects will have the following value
Start: Wed Sep 25 2013 03:00:00 GMT+0300 (GTB Daylight Time)
End: Fri Sep 27 2013 02:59:58 GMT+0300 (GTB Daylight Time)
This is what i am getting back from the server
Start: "2013-09-25T00:00:00.000"
End: "2013-09-26T23:59:58.000"
How can i make sure that the hours in my object are not modified?
EDIT:
There is a a good explaniation here on what's happening with the datetime in javascript.
In the end i used this snipped to solve my problem:
breeze.DataType.parseDateFromServer = function (source) {
var date = moment(source);
return date.toDate();
};
It override's breeze own function with adds a time offset to the datetime.
Breeze does not manipulate the datetimes going to and from the server in any way EXCEPT to add a UTZ timezone specifier to any dates returned from the server that do not already have one. This is only done because different browsers interpret dates without a timezone specifier differently and we want consistency between browsers.
This is discussed in more detail in the answer posted here.
You are passing ISO8601 formatted timestamps, which is good. When you pass the Z at the end, you are indicating that the timestamp represents UTC. When you load those into JavaScript, it's going to take that into account.
You still need to show more code if you are looking for a useful response. What you've currently described from .NET doesn't quite line up with the timestamps you've provided. And it seems like most of this problem has to do with JavaScript and you haven't yet shown any of that code, so I can only guess what you might be doing. Please update your question, and understand that we have no knowledge of your system other than what you show us.
It's possible you may find moment.js to be useful in this scenario, but I can't elaborate further without seeing the relevent JavaScript code.
I'm getting some troubles with the tag and then updating my Date attribute from a model with the params sent.
Here is my tag:
<g:datePicker name="data" value="${controle.data}" precision="month"
years="${(Calendar.getInstance().get(Calendar.YEAR)-70)..Calendar.getInstance().get(Calendar.YEAR)}"/>
When I println the "params.data" it says "struct", but I can't simply do:
model.data = params.data
the params comes with params.data_month and params.data_year with the respectives values in String like:
[[data:struct], [data_month:1], [data_year:2009]]
I tried to do then:
model.data = new SimpleDateFormat("MM/yyyy").parse("${params.data_month}/${params.data_year}")
but it rejects the value, alerting: "Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property 'data'"
println model.data
println new SimpleDateFormat("MM/yyyy").parse("${params.data_month}/${params.data_year}")
--shows
2006-01-01 00:00:00.0
Sun Jan 01 00:00:00 BRST 2006
but I can't simply do: model.data =
params.data
Why not? Are you getting an exception, or are you just being mislead by outdated documentation? -
This is a feature since Grails 1.2. Conversion to a Date type will be performed, automatically.
As for the SimpleDateFormat issue, just add a day, like so:
model.data = new SimpleDateFormat("d/MM/yyyy").parse(
"1/${params.data_month}/${params.data_year}")