I'm trying to bind Date params in my controller. I read somewhere that it should be sufficient if I have the date in format yyyy-MM-dd HH:mm:ss.S. But this doesn't work. The other option that I read was to add _day, _month, _year, etc. suffixes to the attributes that I want to parse, but this method doesn't work either.
So it's basically a one-to-many relationship, when on the many side is the date (an Action has a VisitList<Visit> and the Visit has date).
// domain classes
class Action {
List<Visit> visitList
static hasMany = [visitList: Visit]
}
class Visit {
Action action
static belongsTo = [action: Action]
Date date
String description
}
I parse all the params to the action instance like this:
// in controller
def save(){
Action actionInstance = new Action()
action.properties = params
action.save()
}
The date is sent from view and has this format:
visitList[0].date=2012-05-15 00:00:00.0000
But the date is still null. The visit also has a description attribute and it works well. The param for this attribute is:
visitList[0].description=description
Thanks for any suggestions or advices.
Did you try saving the Visit first and then save the Action? If that doesn't work you may have to convert the date from string to Date, Date.parse() method is depreciated in grails 2.0, try this:
Date date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(dateString)
def action = new Action()
def visit = new Visit(date: date, description: description?:"", action: action)
visit.save()
action.visitList.add(visit)
action.save()
-HTH
Without seeing more code it's difficult to tell. You may want to try a Command Object. Where is your input coming from? A View? If so, one typically uses the and tags to control the input. If you're not using those, you may have to manually parse the date using DateFormat {hint, use the parse() method}.
Related
I want to post activities with custom datetimes.
From what I see in ActivityPostServiceImpl.java, datetimes are created there, and cannot be customized.
Here's a snippet of the code there:
try {
Date postDate = new Date();
ActivityPostEntity activityPost = new ActivityPostEntity();
activityPost.setUserId(userId);
activityPost.setSiteNetwork(tenantService.getName(siteId));
activityPost.setAppTool(appTool);
activityPost.setActivityData(activityData);
activityPost.setActivityType(activityType);
activityPost.setPostDate(postDate);
activityPost.setStatus(status.toString());
activityPost.setLastModified(postDate);
I would like to pass custom datetimes to activities, can be through the activityData object. Yet, I can't make Alfresco to load my new class (which extends ActivityPostServiceImpl) on the beans.
Any help?
The solution was to create a new CustomActivityPostService.java file, where I could extend a postActivity method to have an additional date parameter, and then modify the beans to user that service instead of the default ActivityPostService.
How to pass UTC dates to Web API?
Passing 2010-01-01 works fine, but when I pass a UTC date such as 2014-12-31T22:00:00.000Z (with a time component), I get a HTTP 404 response. So
http://domain/api/controller/action/2012-12-31T22:00:00.000Z
yields a 404 error response, while
http://domain/api/controller/action/2012-12-31
works fine.
How to pass UTC dates to Web API then - or at least specify date and time?
The problem is twofold:
1. The . in the route
By default, IIS treats all URI's with a dot in them as static resource, tries to return it and skip further processing (by Web API) altogether. This is configured in your Web.config in the section system.webServer.handlers: the default handler handles path="*.". You won't find much documentation regarding the strange syntax in this path attribute (regex would have made more sense), but what this apparently means is "anything that doesn't contain a dot" (and any character from point 2 below). Hence the 'Extensionless' in the name ExtensionlessUrlHandler-Integrated-4.0.
Multiple solutions are possible, in my opinion in the order of 'correctness':
Add a new handler specifically for the routes that must allow a dot. Be sure to add it before the default. To do this, make sure you remove the default handler first, and add it back after yours.
Change the path="*." attribute to path="*". It will then catch everything. Note that from then on, your web api will no longer interpret incoming calls with dots as static resources! If you are hosting static resources on your web api, this is therefor not advised!
Add the following to your Web.config to unconditionally handle all requests: under <system.webserver>: <modules runAllManagedModulesForAllRequests="true">
2. The : in the route
After you've changed the above, by default, you'd get the following error:
A potentially dangerous Request.Path value was detected from the client (:).
You can change the predefined disallowed/invalid characters in your Web.config. Under <system.web>, add the following: <httpRuntime requestPathInvalidCharacters="<,>,%,&,*,\,?" />. I've removed the : from the standard list of invalid characters.
Easier/safer solutions
Although not an answer to your question, a safer and easier solution would be to change the request so that all this is not required. This can be done in two ways:
Pass the date as a query string parameter, like ?date=2012-12-31T22:00:00.000Z.
Strip the .000 from every request, and encode the url, so replace all :'s with %3A, e.g. by using HttpUtility.UrlEncode().
in your Product Web API controller:
[RoutePrefix("api/product")]
public class ProductController : ApiController
{
private readonly IProductRepository _repository;
public ProductController(IProductRepository repository)
{
this._repository = repository;
}
[HttpGet, Route("orders")]
public async Task<IHttpActionResult> GetProductPeriodOrders(string productCode, DateTime dateStart, DateTime dateEnd)
{
try
{
IList<Order> orders = await _repository.GetPeriodOrdersAsync(productCode, dateStart.ToUniversalTime(), dateEnd.ToUniversalTime());
return Ok(orders);
}
catch(Exception ex)
{
return NotFound();
}
}
}
test GetProductPeriodOrders method in Fiddler - Composer:
http://localhost:46017/api/product/orders?productCode=100&dateStart=2016-12-01T00:00:00&dateEnd=2016-12-31T23:59:59
DateTime format:
yyyy-MM-ddTHH:mm:ss
javascript pass parameter use moment.js
const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss');
const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');
I feel your pain ... yet another date time format... just what you needed!
Using Web Api 2 you can use route attributes to specify parameters.
so with attributes on your class and your method you can code up a REST URL using this utc format you are having trouble with (apparently its ISO8601, presumably arrived at using startDate.toISOString())
[Route(#"daterange/{startDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}/{endDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}")]
[HttpGet]
public IEnumerable<MyRecordType> GetByDateRange(DateTime startDate, DateTime endDate)
.... BUT, although this works with one date (startDate), for some reason it doesnt work when the endDate is in this format ... debugged for hours, only clue is exception says it doesnt like colon ":" (even though web.config is set with :
<system.web>
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" requestPathInvalidCharacters="" />
</system.web>
So, lets make another date format (taken from the polyfill for the ISO date format) and add it to the Javascript date (for brevity, only convert up to minutes):
if (!Date.prototype.toUTCDateTimeDigits) {
(function () {
function pad(number) {
if (number < 10) {
return '0' + number;
}
return number;
}
Date.prototype.toUTCDateTimeDigits = function () {
return this.getUTCFullYear() +
pad(this.getUTCMonth() + 1) +
pad(this.getUTCDate()) +
'T' +
pad(this.getUTCHours()) +
pad(this.getUTCMinutes()) +
'Z';
};
}());
}
Then when you send the dates to the Web API 2 method, you can convert them from string to date:
[RoutePrefix("api/myrecordtype")]
public class MyRecordTypeController : ApiController
{
[Route(#"daterange/{startDateString}/{endDateString}")]
[HttpGet]
public IEnumerable<MyRecordType> GetByDateRange([FromUri]string startDateString, [FromUri]string endDateString)
{
var startDate = BuildDateTimeFromYAFormat(startDateString);
var endDate = BuildDateTimeFromYAFormat(endDateString);
...
}
/// <summary>
/// Convert a UTC Date String of format yyyyMMddThhmmZ into a Local Date
/// </summary>
/// <param name="dateString"></param>
/// <returns></returns>
private DateTime BuildDateTimeFromYAFormat(string dateString)
{
Regex r = new Regex(#"^\d{4}\d{2}\d{2}T\d{2}\d{2}Z$");
if (!r.IsMatch(dateString))
{
throw new FormatException(
string.Format("{0} is not the correct format. Should be yyyyMMddThhmmZ", dateString));
}
DateTime dt = DateTime.ParseExact(dateString, "yyyyMMddThhmmZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
return dt;
}
so the url would be
http://domain/api/myrecordtype/daterange/20140302T0003Z/20140302T1603Z
Hanselman gives some related info here:
http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
As a similar alternative to s k's answer, I am able to pass a date formatted by Date.prototype.toISOString() in the query string. This is the standard ISO 8601 format, and it is accepted by .Net Web API controllers without any additional configuration of the route or action.
e.g.
var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
This is a solution and a model for possible solutions. Use Moment.js in your client to format dates, convert to unix time.
$scope.startDate.unix()
Setup your route parameters to be long.
[Route("{startDate:long?}")]
public async Task<object[]> Get(long? startDate)
{
DateTime? sDate = new DateTime();
if (startDate != null)
{
sDate = new DateTime().FromUnixTime(startDate.Value);
}
else
{
sDate = null;
}
... your code here!
}
Create an extension method for Unix time. Unix DateTime Method
It used to be a painful task, but now we can use toUTCString():
Example:
[HttpPost]
public ActionResult Query(DateTime Start, DateTime End)
Put the below into Ajax post request
data: {
Start: new Date().toUTCString(),
End: new Date().toUTCString()
},
As a matter of fact, specifying parameters explicitly as ?date='fulldatetime' worked like a charm. So this will be a solution for now: don't use commas, but use old GET approach.
One possible solution is to use Ticks:
public long Ticks { get; }
Then in the controller's method:
public DateTime(long ticks);
Since I have encoding ISO-8859-1 operating system the date format "dd.MM.yyyy HH:mm:sss" was not recognised what did work was to use InvariantCulture string.
string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)
By looking at your code, I assume you do not have a concern about the 'Time' of the DateTime object. If so, you can pass the date, month and the year as integer parameters. Please see the following code. This is a working example from my current project.
The advantage is; this method helps me to avoid DateTime format issues and culture incompatibilities.
/// <summary>
/// Get Arrivals Report Seven Day Forecast
/// </summary>
/// <param name="day"></param>
/// <param name="month"></param>
/// <param name="year"></param>
/// <returns></returns>
[HttpGet("arrivalreportsevendayforecast/{day:int}/{month:int}/{year:int}")]
public async Task<ActionResult<List<ArrivalsReportSevenDayForecastModel>>> GetArrivalsReportSevenDayForecast(int day, int month, int year)
{
DateTime selectedDate = new DateTime(year, month, day);
IList<ArrivalsReportSevenDayForecastModel> arrivingStudents = await _applicationService.Value.GetArrivalsReportSevenDayForecast(selectedDate);
return Ok(arrivingStudents);
}
If you are keen to see the front-end as well, feel free to read the code below. Unfortunately, that is written in Angular. This is how I normally pass a DateTime as a query parameter in Angular GET requests.
public getArrivalsReportSevenDayForecast(selectedDate1 : Date): Observable<ArrivalsReportSevenDayForecastModel[]> {
const params = new HttpParams();
const day = selectedDate1.getDate();
const month = selectedDate1.getMonth() + 1
const year = selectedDate1.getFullYear();
const data = this.svcHttp.get<ArrivalsReportSevenDayForecastModel[]>(this.routePrefix +
`/arrivalreportsevendayforecast/${day}/${month}/${year}`, { params: params }).pipe(
map<ArrivalsReportSevenDayForecastModel[], ArrivalsReportSevenDayForecastModel[]>(arrivingList => {
// do mapping here if needed
return arrivingList;
}),
catchError((err) => this.svcError.handleError(err)));
return data;
}
Passing the date as a string and then parsing it worked for me. Probably want to add try catch on the parse, but this is the basic code.
[HttpGet("name={name}/date={date}", Name = "GetByNameAndDate")]
public IActionResult GetByNameAndDate(string name, string date) {
DateTimeOffset dto = DateTimeOffset.Parse(date);
}
Then the request can look like this
https://localhost/api/Contoller/name=test/date=2022-02-18T13:45:37.000Z
For external APIs (where you do not know what type of client will call your service), Unix Time should be used both on the input parameters and outputted date fields.
https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tounixtimeseconds?view=net-6.0
.Net provides ToUnixtimeSeconds and FromUnixtimeSeconds to easily convert to DateTime or DateTimeOff
Unix Time should be preferred over ISO formats because it is just a integer and can be passed in the URL string without encoding.
The 'Ticks' property is similar to Unix time but (I believe) should only be use between a .net client and server.
Most well know APIs will use Unix Time, for example see Stripe's API:
https://stripe.com/docs/api
The obvious downsides of using Unix time are:
They are not human readable
They cannot be created by humans - making it difficult to call the API without code
Use binary format.
to send the info in url use dateTimeVar.ToBinary()
it will be something like
http://domain/api/controller/action/637774955400000000
when you reciebe the data will get like Long variable and use the static function of DateTime Class to transform to DateTime type again.
DateTime MyDateTime = DateTime.FromBinary(BinaryDateTime);
Cheers
Being kind of a newb to MVC 4 (or really any of the MVC's for ASP.NET) I cant help but feel theres more to the URL helper than what I'm seeing.
Basically I've read the tutorials on populating the attributes in a controllers methods using a query string in the URL.
I dont liek query strings though and prefer a sectioned "folder" like style.
Without much further adu, this is the sample URL:
http://something.com/DataTypes/Search/searchString
this approach is actually pretty safe as there will only ever be single worded searches
I have tried in the DataTypes controller
[HttpGet]
public ActionResult Search(String q)
{
ViewBag.ProvidedQuery = q;
return View();
}
and a few other small variations, right now im just trying to get the string to show up in the view but I dont seem to be getting anything there.
Is there anyway to inject the 3rd string in the url into an attribute?
If not, which URL helper class am I supposed to use to acquire the string data in the URL? Even if I have to parse the whole URL manually so be it, i just need to acquire that 3rd element in the URL as a string
Extremely n00b question im sure, but either im not finding this simple guide somewhere, or im not searching google correctly...
What you're missing is that the default route parameter name is "id". So you want to do this:
[HttpGet]
public ActionResult Search(String id)
{
ViewBag.ProvidedQuery = id;
return View();
}
If you don't want to use the variable name id, then you can modify your Route to look like this:
routes.MapRoute(
name: "Search",
url: "DataTypes/Search/{searchString}",
defaults: new { controller = "DataTypes", action = "Search",
searchString = UrlParameter.Optional });
If you don't want the string to be optional, then you can remove the last field from the defaults object.
you can use RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)) to get the routedata
String URL to RouteValueDictionary
You need to look at the routing in the Global.asax.cs. For example for your case you could add a route to the routes collection like this:
routes.MapRoute("Search",
"DataTypes/Search/{q}",
new { controller = "DataTypes", action = "Search" }
);
Then the q parameter will automatically get mapped to your action. The default controller mapping is likely mapping it to "id".
I know that it's possible to register a custom property editor, as demonstrated here. It is my understanding that this will cause all properties for the registered type to be bound using that custom editor.
(Perhaps that's a misunderstanding on my part? If so, enlighten me!)
However, what if I only want to register a custom editor for a single property on a given domain?
Example:
class MyCommand {
Date foo // bind this using a custom format, e.g. 'yyyy-MM-dd'
Date bar // bind this using the normal 'struct' date picker fields
}
class MyController {
def myAction = { MyCommand command ->
// params has [foo: '2011-01-01', bar: 'struct', ...]
// which get bound to command as above
}
}
Does Grails have a built-in way to do this?
I know that it's possible to register a custom property editor, as demonstrated here. It is my understanding that this will cause all properties for the registered type to be bound using that custom editor.
AFAIK, this is correct
However, what if I only want to register a custom editor for a single property on a given domain?
If you're using Grails 2.3.0 or later, you can do this like so:
class MyCommand {
#BindingFormat('yyyy-MM-dd')
Date foo // bind this using a custom format, e.g. 'yyyy-MM-dd'
Date bar // bind this using the normal 'struct' date picker fields
}
You could write getter and setters that take the format you want and return the format you want. The DateFormatter or SimpleDateFormatter classes would be useful in your get/set methods.
You can still use the neat trick for dates in grails:
<g:form>
<g:hiddenField name="myDomainInstance.bar_year" value="2011"/>
<g:hiddenField name="myDomainInstance.bar_month" value="07"/>
<g:hiddenField name="myDomainInstance.bar_day" value="01"/>
<g:textField name="myDomainInstance.bar_hour" value=""/>
<g:textField name="myDomainInstance.bar_minute" value=""/>
</g:form>
In controller:
myDomainInstance.properties = params.myDomainInstance
Will result in the desired date for bar.
If I have in my model class a property of type DateTime how can I render it in a specific format - for example in the format which ToLongDateString() returns?
I have tried this...
#Html.DisplayFor(modelItem => item.MyDateTime.ToLongDateString())
...which throws an exception because the expression must point to a property or field. And this...
#{var val = item.MyDateTime.ToLongDateString();
Html.DisplayFor(modelItem => val);
}
...which doesn't throw an exception, but the rendered output is empty (although val contains the expected value, as I could see in the debugger).
Thanks for tips in advance!
Edit
ToLongDateString is only an example. What I actually want to use instead of ToLongDateString is a custom extension method of DateTime and DateTime?:
public static string FormatDateTimeHideMidNight(this DateTime dateTime)
{
if (dateTime.TimeOfDay == TimeSpan.Zero)
return dateTime.ToString("d");
else
return dateTime.ToString("g");
}
public static string FormatDateTimeHideMidNight(this DateTime? dateTime)
{
if (dateTime.HasValue)
return dateTime.Value.FormatDateTimeHideMidNight();
else
return "";
}
So, I think I cannot use the DisplayFormat attribute and DataFormatString parameter on the ViewModel properties.
You could decorate your view model property with the [DisplayFormat] attribute:
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}",
ApplyFormatInEditMode = true)]
public DateTime MyDateTime { get; set; }
and in your view:
#Html.EditorFor(x => x.MyDate)
or, for displaying the value,
#Html.DisplayFor(x => x.MyDate)
Another possibility, which I don't recommend, is to use a weakly typed helper:
#Html.TextBox("MyDate", Model.MyDate.ToLongDateString())
If all you want to do is display the date with a specific format, just call:
#String.Format(myFormat, Model.MyDateTime)
Using #Html.DisplayFor(...) is just extra work unless you are specifying a template, or need to use something that is built on templates, like iterating an IEnumerable<T>. Creating a template is simple enough, and can provide a lot of flexibility too. Create a folder in your views folder for the current controller (or shared views folder) called DisplayTemplates. Inside that folder, add a partial view with the model type you want to build the template for. In this case I added /Views/Shared/DisplayTemplates and added a partial view called ShortDateTime.cshtml.
#model System.DateTime
#Model.ToShortDateString()
And now you can call that template with the following line:
#Html.DisplayFor(m => m.MyDateTime, "ShortDateTime")
Simple formatted output inside of the model
#String.Format("{0:d}", model.CreatedOn)
or in the foreach loop
#String.Format("{0:d}", item.CreatedOn)
I use the following approach to inline format and display a date property from the model.
#Html.ValueFor(model => model.MyDateTime, "{0:dd/MM/yyyy}")
Otherwise when populating a TextBox or Editor you could do like #Darin suggested, decorated the attribute with a [DisplayFormat] attribute.
If all your DateTime types are rendered the same way you can use a custom DateTime display template.
In your Views folder create a folder named "DisplayTemplates" either under your controller specific views folder, or under "Shared" folder (these work similar to partials).
Inside create a file named DateTime.cshtml that takes DateTime as the #model and code how you want to render your date:
#model System.DateTime
#Model.ToLongDateString()
Now you can just use this in your views and it should work:
#Html.DisplayFor(mod => mod.MyDateTime)
As long as you follow the convention of adding it to the "DisplayTemplates" folder and naming the file to match the type your are displaying, MVC will automatically use that to display your values. This also works for editing scenarios using "EditorTemplates".
Here's some more information on templates.
My preference is to keep the formatting details with the view and not the viewmodel. So in MVC4/Razor:
#Html.TextBoxFor(model => model.DateTime, "{0:d}");
datetime format reference:
http://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.71).aspx
Then I have a JQuery datepicker bound to it, and that put's the date in as a different format...doh!
Looks like I need to set the datepicker's format to the same formatting.
So I'm storing the System.Globalization formatting in a data-* attribute and collecting it when setting up the
#Html.TextBoxFor(
model => model.DateTime.Date,
"{0:d}",
new
{
#class = "datePicker",
#data_date_format=System.Globalization.CultureInfo
.CurrentUICulture.DateTimeFormat.ShortDatePattern
}));
And here's the sucky part: the formats of .net and datepicker do not match, so hackery is needed:
$('.datePicker').each(function(){
$(this).datepicker({
dateFormat:$(this).data("dateFormat").toLowerCase().replace("yyyy","yy")
});
});
that's kind of weak, but should cover a lot of cases.
works for me
<%=Model.MyDateTime.ToString("dd-MMM-yyyy")%>
Had the same problem recently.
I discovered that simply defining DataType as Date in the model works as well (using Code First approach)
[DataType(DataType.Date)]
public DateTime Added { get; set; }
In MVC5 I'd use, if your model is the datetime
string dt = Model.ToString("dd/MM/yyy");
Or if your model contains the property of the datetime
string dt = Model.dateinModel.ToString("dd/MM/yyy");
Here's the official meaning of the Formats:
https://msdn.microsoft.com/en-us/library/8kb3ddd4(v=vs.110).aspx
you can do like this #item.Date.Value.Tostring("dd-MMM-yy");
if I just want to display the date in short format I just use
#Model.date.ToShortDateString() and it prints the date in
If all you want to do is display the date with a specific format, just call:
#Model.LeadDate.ToString("dd-MMM-yyyy")
#Model.LeadDate.ToString("MM/dd/yy")
It will result in following format,
26-Apr-2013
04/26/13
this will display in dd/MM/yyyy format in your View
In View:
instead of DisplayFor use this code
<td>
#(item.Startdate.HasValue ? item.Startdate.Value.ToString("dd/MM/yyyy") : "Date is Empty")
</td
it also checks if the value is null in date column, if true then it will display Date is Empty or the actual formatted date from the column.
Hope helps someone.
#{
string datein = Convert.ToDateTime(item.InDate).ToString("dd/MM/yyyy");
#datein
}
Only View File Adjust like this. You may try this.
#Html.FormatValue( (object)Convert.ChangeType(item.transdate, typeof(object)),
"{0: yyyy-MM-dd}")
item.transdate it is your DateTime type data.