I am trying to save the date the user enters into a database. The problem is that despite only the date showing in the text box the time is also being saved. This is what it looks like in my model.
{11/02/2015 00:00:00}
However this is what I have in my JQuery set up and how I would like it to look when saved to the database, without the time also attached.
$("#datepicker").datepicker({ dateFormat: 'dd/mm/yy' });
I assume the problem lies in my model somewhere so this is how I have it set up at the moment.
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DateOfBooking { get; set; }
and this is my view,
<div class="form-group">
#Html.LabelFor(model => model.DateOfBooking, new { #class = "control-label col-md-2"})
<div class="col-md-10">
#Html.TextBoxFor(x => x.DateOfBooking, new { #id = "datepicker" })
#Html.ValidationMessageFor(model => model.DateOfBooking)
</div>
</div>
There's no way, using DateTimes, to only store/pass the date to the model. In SQL you can use a Date data type that will only store the Date part of the DateTime object from .Net. However, when you move data into/out of your model you'll always have a time component. The easiest way is to just ignore the time component and specify the display format like you're doing.
You could also use something like Noda Time that has structs for Date only types.
Related
Edit 1
Just to Clarify a couple of points,
The datetime picker/script appears to be working fine, the times and dates are selectable and fill the textbox with the correct values as expected.
The issue occurs when a time/date is manually entered into the textbox by the user and an invalid time is entered (i.e. "78/5/2017 12:00" or "12/5/2017 12:62")
I've updated a typo in the code, the error is still occurring with the corrected code.
I have my fingers crossed I'm missing something obvious because this makes no sense to me. Any help you can offer would be much appreciated. I have included my code at the end of the question.
The Problem
I have two DateTime fields which are included in a form on a page in my application: StartTime and EndTime.
Both of the fields are setup and put onto the page using (what I believe to be) identical code.
The StartTime field works perfectly, only excepting valid times as input and displaying an error for nonsensical times such as 28:30 or 17:67 until the User corrects it.
The EndTime field however does not validate correctly. Bad inputs are switched back to the current Time/Date before being passed back to the controller, the controller never even sees the bad values meaning I can't catch it and return an error at that point.
If nonsensical values are given in both fields then the submission is prevented and both fields show a validation error which suggests that the EndTime validation does work, it just doesn't prevent form submission.
My Efforts
As I have one working field I have attempted to use that to correct the error. However I hit a stumbling block in realising that there are no differences between the two. Deciding that I must have missed something I switched the variable names round so that the StartTime would be using the EndTime code and vice versa, I did this in each of the sections below one by one hoping to find a point where the field which was working swapped. That, however, never happened. Even once the entirety of their code was switched over it was still found to be the EndTime variable/field which was broken and the StartTime variable/field which was working.
My Research
Despite spending nearly a week with this bug now I have been unable to find any similar problems online and am at a complete stumbling block as to where to go or what to try now. I have tried looking for issues caused by DateTime calendar pickers as well as validation errors in general but can't find anything of use to this situation.
This is one of the last bugs to fix before the project is completed and so any help or even ideas you can offer would be amazing.
The Code
I have included everything I could think to here that interacts with the fields in question. If I have missed anything or you need more info please let me know.
The Entity Model
I have the following two DateTime fields in my Record Entity
public partial class Record
{
// Other entity fields
// ....
// ...
// ..
[DisplayName("Start Time")]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:g}", ApplyFormatInEditMode = true)]
public DateTime StartTime { get; set; }
[DisplayName("End Time")]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:g}", ApplyFormatInEditMode = true)]
public DateTime EndTime { get; set; }
// and in the constructor
public Record()
{
// initialise the DateTime fields with the current DateTime,
// adjusted for daylight savings
BaseController b = new BaseController();
StartTime = b.TimeNow();
EndTime = b.TimeNow();
}
}
For the sake of completion this is the TimeNow() function's code:
public DateTime TimeNow()
{
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime t = DateTime.Now;
if (tz.IsDaylightSavingTime(t))
t = t.AddHours(1);
return t;
}
The ViewModel
The Record entity is then included into a ViewModel as follows:
public class Home_UserAddRecord
{
[DisplayName("Record")]
public Record record { get; set; }
// Other ViewModel fields
// ....
// ...
// ..
// and the blank constructor:
public Home_UserAddRecord()
{
record = new Record();
Error = false;
ErrorMessage = string.Empty;
}
}
The CSHTML Form
They are then included into a form on the page like so:
#using (Html.BeginForm())
{
<div class="form-horizontal">
<div class="form-group col-md-12">
#Html.LabelFor(model => model.record.StartTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.record.StartTime, new { htmlAttributes = new { #Value = Model.record.StartTime.ToString("dd/MM/yyyy HH:mm"), #class = "form-control", #id = "StartDate" } })
#Html.ValidationMessageFor(model => model.record.StartTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group col-md-12">
#Html.LabelFor(model => model.record.EndTime, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.record.EndTime, new{ htmlAttributes = new{ #Value = Model.record.EndTime.ToString("dd/MM/yyyy HH:mm"), #class = "form-control", #id = "EndDate" } })
#Html.ValidationMessageFor(model => model.record.EndTime, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
An Attached Script
And then finally they have a script applied to them to allow the use of a calendar picker on each input. The script looks like this:
#section Scripts{
<script>
var Start = new dhtmlXCalendarObject("StartDate");
Start.setDateFormat("%d/%m/%Y %H:%i");
Start.showToday();
Start.attachEvent("onTimeChange", function (d) {
var DateText = Start.getDate(true)
document.getElementById("StartDate").value = DateText;
});
var End = new dhtmlXCalendarObject("EndDate");
End.setDateFormat("%d/%m/%Y %H:%i");
End.showToday();
End.attachEvent("onTimeChange", function (d) {
var DateText = End.getDate(true)
document.getElementById("EndDate").value = DateText;
});
</script>
}
Perhaps a suggestion is to use DateTime.TryParseExact method, which will validate the "String" representation of the date using your desired format, and will return an error when the string does not comply to your specified format. Here is code, note dateFormats are based on Australian Standard dates. You can of course add hours and minutes to this too.
Note parsedDate is a DateTime format. Usage of below is:
public void test(){
DateTime ParsedDate;
string SomeDate = "12-May-2017";
if(parseDate(SomeDate, out ParsedDate))
{
// Date was parsed successfully, you can now used ParsedDate, e.g.
Customer.Orders[0].DateRequired = ParsedDate;
}
else
{
// Throw an error
}
}
And the method declaration. Use either in static class, or directly in your class.
public static bool parseDate(string theDate, out DateTime parsedDate)
{
string[] dateFormats = { "d-M-yy", "d-MMM-yy", "d-MMM-yyyy", "d-M-yyyy", "d/M/yy", "d/M/yyyy", "yyyy-mm-dd" };
bool result = DateTime.TryParseExact(
theDate,
dateFormats,
new CultureInfo("en-AU"),
DateTimeStyles.None, out parsedDate);
return result;
} //Convert string-based date to DateTime. Uses a variety of parse templates
I once faced a similar issue where view was submitting the model even if validation failed. I happened to be not using ModelState.IsValid() in the controller action. Do check if this helps.
#if (Model.RecDateFrom.HasValue)
{
#Html.EditorFor(model => model.RecDateFrom,
new {htmlAttributes =
new {#Value = Model.RecDateFrom.Value.ToString("yyyy-MM-dd"),
#class = "form-control input-sm small-input-fix"}})
}
else
{
#Html.EditorFor(model => model.RecDateFrom,
new {htmlAttributes = new {#class = "form-control input-sm small-input-fix"}})
}
#Html.ValidationMessageFor(model => model.RecDateFrom, "", new {#class = "text-danger"})
You can see above how I have to handle if the datetime is null before setting the value. I have to set the value because MVC uses the incorrect format for a date input, making it so chrome doesn't have the correct default value.
I do not want to use the accepted solution in this question because that changes the format of the display for also.
I've tried using editor templates, but it seems like you have to start from scratch, rather than extending the built in editor template for Date datatypes (this seems like a large flaw of MVC, unless I'm missing something).
If you are wanting to render the browsers HTML5 datepicker, you just need to apply the correct attributes to you property. Note the format string must be in the ISO format.
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime RecDateFrom { get; set; }
and then in the view
#Html.EditorFor(m => m.RecDateFrom, new { htmlAttributes = new { #class = "form-control input-sm small-input-fix" } })
Side note: The HTML5 datepicker is not supported in older browsers and not yet at all in FireFox, so it may be better (at least in the short term) to use a jquery plugin (and set the format in the plugin initializer). For example, using the jquery ui datepicker - $('#datepicker').datepicker({ dateFormat: 'dd-mm-yy' })
I have a textbox on my view and I'm only looking it to display the current date and time(in readonly..how can this be done?
Currently I have this in the view:
<div class="editor-field">
#Html.EditorFor(model => model.Opened, new { #value = System.DateTime.Now, #readonly = "readonly" })
#Html.ValidationMessageFor(model => model.Opened)
</div>
..And this in the model:
[Required]
public DateTime Opened
{
get;
set;
}
How can this be implemented in MVC?
Thanks,
I recommend you using a Label. I think it's unusual display dates and time at TextBoxes.
Regards.
Well, you can do that, but #Angel Manuel Garcia Car's suggest is what you should be doing logically.
Anyways, here is the code. As long as you are creating a textbox, you should be using TextBoxFor.
#Html.TextBoxFor(model => model.Opened, new { #value = DateTime.Now, #readonly="readonly" })
I don't see any point here.
I have an application that warrants the need for users to supply a "best fit time of day" for a particular day of week. I am trying to represent that time of day as the timespan object in C# (just as the DateTime.TimeOfDay object does) I don't need the date, but also don't want to use datetime and have the user see an actual date.
Right now i have this in my model:
[Required, Display(Name = "Start")]
public TimeSpan StartTime { get; set; }
[Required, Display(Name = "End")]
public TimeSpan EndTime { get; set; }
I have this for part of my view:
<div class="form-group col-xs-6 col-lg-4">
#Html.LabelFor(m => m.StartTime, new { #class = "control-label" })
#(Html.Kendo().TimePickerFor(m => m.StartTime).Interval(15).Culture("en-Us").HtmlAttributes(new { #class = "form-control" }))
#Html.ValidationMessageFor(m => m.StartTime)
</div>
<div class="clearfix"></div>
<div class="form-group col-xs-6 col-lg-4">
#Html.LabelFor(m => m.EndTime, new { #class = "control-label" })
#(Html.Kendo().TimePickerFor(m => m.EndTime).Interval(15).Culture("en-Us").HtmlAttributes(new { #class = "form-control" }))
#Html.ValidationMessageFor(m=>m.EndTime)
</div>
<div class="clearfix"></div>
I keep getting this model error using the MVC ModelState:
ErrorMessage = "The value '1:00 PM' is not valid for Start."
I'm not sure what i'm doing wrong, intellesense says that the kendo widget supports a timespan for it's input, but why isn't this actually binding to the view?
i have seen this article: Kendo Timepickerfor formatting not working
It's not quite what i'm looking for, because i feel like there should be a more simple way to represent a time of the day that is unrelated to the actual date...
Any help is greatly appreciated!
Thanks!
Using DateTime seemed to work like you had said #Saranga
Not too thrilled it stores the entire date in the database, as it's not immediately clear then that this is only relevant to the time only, but it works.
Thanks for your help!
My model has a object which has a date property...
[Required(ErrorMessage="{0} is required")]
[Display(Name="Donation date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime DonationDate { get; set; }
The view has an editor for it....
<div class="editor-label">
#Html.LabelFor(model => model.Donation.DonationDate)
</div>
<div class="editor-field">
#Html.TextBox("txtDate", Model.Donation.DonationDate.ToString("dd/MM/yyyy"), new { #class = "date" })
#Html.ValidationMessageFor(model => model.Donation.DonationDate)
</div>
The controller just receives back the model... but when I do this in the controller and give it to another view...
ViewBag.Date = model.Donation.DonationDate;
Every time I just get back
1/1/0001 12:00:00 AM no matter what the date was set to. Any thoughts? Thanks!
Forgot to mention... I'm also using JQuery datepicker on the editor field:
$('.date').attr("placeholder", "dd/mm/yy").datepicker({
dateFormat: "dd/mm/yy",
changeMonth: true,
changeYear: true
});
You can use TextBoxFor so that the view engine knows how to label the form field appropriately (so that the model binder will recognize it on postback):
#Html.TextBoxFor(model => model.Donation.DonationDate, new { #class = "date" })
Alternatively, you could name the textbox correctly manually. I'm not sure exactly what that would look like ...
Update
Ok, I as curious so I checked. The naming convention for nested fields uses the dot notation. So you should be able to write this:
#Html.TextBox("Donation.DonationDate", Model.Donation.DonationDate.ToString("dd/MM/yyyy"), new { #class = "date" })
Update #2
To format the correctly, apply an attribute to the DonationDate property in your model:
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode=true)]
For this to work, you also have to use #Html.EditorFor instead of TextBoxFor.