I am new on MVC3 and not familiar with the unit testing part. I have been trying to construct Datetime with exception handling from accepting 3 integer value but the it fails the unit testing. Im not sure i am doing it correctly or not.
This is the controller part:
public DateTime MakeDate(string dateString)
{
DateTime myDate;
if (DateTime.TryParseExact(dateString, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out myDate))
{
return myDate;
}
return new DateTime();
}
And this is the unit Testing:
[TestMethod]
public void MakeDateConstructsADateTimeFromYearMonthAndDay()
{
//Arrange
var controller = new DateController();
var expected = new DateTime(2014, 6, 30);
//Act
var result = controller.MakeDate(2014, 6, 30);
//Assert
Assert.AreEqual<DateTime>(expected, result);
}
[TestMethod]
public void MakeDateReturnsDefaultDateTimeIfInputDataInvalid()
{
var controller = new DateController();
var expected = new DateTime();
//Act
//June has only 30 days so this will cause an exception
var result = controller.MakeDate(2014, 6, 31);
//Assert
Assert.AreEqual<DateTime>(expected, result);
}
Thanks in advance
string date = "2014-06-30";
DateTime datetime = DateTime.ParseExact(date, "yyyyMMdd", CultureInfo.InvariantCulture);
Your date string is not of the format you use in ParseExact.
You are using yyyyMMdd when you should be using yyyy-MM-dd.
The code fails because the string doesn't match the format.
Try changing your MakeDate function to something like this:
DateTime myDate;
if (DateTime.TryParseExact(dateString, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out myDate))
{
return myDate;
}
return new DateTime();
Also, your MakeDate function doesn't use the dr parameter and you specify a date in format yyyy-MM-dd and use ParseExact with a different format (yyyyMMdd).
Related
I want to update the lastsync column the problem is the data wont save properly. I used Convert.ToDateTime and DateTime.Parse and still the data wont save properly. I am getting "01/01/0001 12:00:00". What am I doing wrong?
public DateTime LastUpdated { get; set; }
var current_datetime = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
string ofretailer_group_sql = "UPDATE tblRetailerGroup SET LastUpdated = '" + DateTime.Parse(current_datetime) + "' WHERE RetailerCode = '" + retailerCode + "'";
await conn.ExecuteAsync(ofretailer_group_sql);
If you are getting 01/01/0001 12:00:00 from your TimeDate field, that means it was never set/updated.
Personally I use query (?) parameters to allow the SQL statement strings to become a bunch of constants that never change and then you just have to assign the parameters for that query in the order they fall within the SQL statement:
Example:
const string ofretailer_group_sql = "UPDATE tblRetailerGroup SET LastUpdated = ? WHERE RetailerCode = ?";
await conn.ExecuteAsync(ofretailer_group_sql, new object[] { DateTime.UtcNow, retailerCode });
Note: I am using UtcNow to remove DST and phone device timezone change issues...
You should create dependency services for file management to save and get a data in IOS and Android level. Here is code how to save and get datetime in file.
PCL Project:- Create the interface
public interface IFileHelper
{
void SaveLastUpdatedDateTimeForData(DateTime dateTime);
DateTime GetLastUpdatedDateTimeForData();
}
IOS/Android Project:- Create one Class to save the data in file.
[assembly: Dependency(typeof(FileHelper))]
namespace ABCD.iOS
{
public class FileHelper: IFileHelper
{
public void SaveLastUpdatedDateTimeForData(DateTime dateTime)
{
try {
string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filePath = Path.Combine(docFolder, "..", "Library", "updatetime.txt");
File.WriteAllText(filePath, dateTime.Ticks.ToString());
} catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
}
public DateTime GetLastUpdatedDateTimeForData()
{
//bool status = false;
string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string filePath = Path.Combine(docFolder, "..", "Library", "updatetime.txt");
if (!File.Exists(filePath)) {
return new DateTime(2000, 1, 1);
}
try {
string content = File.ReadAllText(filePath);
DateTime dt = new DateTime(long.Parse(content));
return dt;
} catch (Exception ex) {
Debug.WriteLine("exception in GetLastUpdatedDateTimeForData : {0}", ex.Message);
return DateTime.MinValue;
}
}
}
}
Call the dependency Service in Client/PCL project:
Save updated DateTime:
DependencyService.Get<IFileHelper>().SaveLastUpdatedDateTimeForData(DateTime.Now);
Get Updated DateTime:
var lastUpdatedDateTime = DependencyService.Get<IFileHelper>().GetLastUpdatedDateTimeForData();
It seems to be a conversion issue from Date-String-Date.
I would go for directly saving first DateTime.now in Db and check if the value is saved correctly. If that works then definitely its a conversion issue. Then you can try below solution.
I do conversion like this:
DateTime date = DateTime.ParseExact(current_datetime, "yyyy-MM-dd hh:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
private List<Activity> sortByDate(List<Activity> activityList){
System.out.println(activityList.size());
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
Collections.sort(activityList, new Comparator<Activity>() {
public int compare(Activity a1, Activity a2) {
System.out.println("aaaaaaaaaaaaa");
int result = 0;
try {
result = sdf.parse(a2.getDate()).compareTo(sdf.parse(a1.etDate()));
} catch (ParseException e) {
//The date should be validated to (dd-MM-yyyy HH:mm:ss) this format in front end before add to the db
}
return result;
}
});
return activityList;
}
This is the method that I have created. The first SOP printed 2 as output so activityList is not empty, but the second SOP line wasn't executed. The code that I use to call the sortByDate method is below. I can't figure out why Collections.sort is not executed.
public List<RetrieveActivity> getPage(int pageNumber) throws CustomException{
PageRequest request = new PageRequest(pageNumber - 1, PAGESIZE, Sort.Direction.ASC, "id");
List<Activity> tempList = repository.findAll(request).getContent();
if (tempList.size() > 0) {
tempList = sortByDate(tempList);
}
But Activity model class has the date attribute as a String. But the thing is, the line 'System.out.println("aaaaaaaaaaaaa");' was never executed.
The pattern that you're using ("dd-MM-yyyy HH:mm:ss") doesn't maintain order when you're moving to the string representation of the object.
Instead of using string comparison:
result = sdf.parse(a2.getDate()).compareTo(sdf.parse(a1.etDate()));
use date comparison:
result = a2.getDate().compareTo(a1.getDate());
I want to concatenate DateTime field and string field in MVC application.
I want Linq to Entities query to solve this. Here is my SQL query which I want in Linq.
Select accountid,TourID, ' ('+ convert(nvarchar(20), fromdate ,105) +')' + purpose as TourDetails
from Tour
where AccountID=#AccID;
As shown in above query I want to concat fromdate and purpose. I want to pass result of this query as JSON result.
Something like this:
public class Tour
{
public int accountid { get; set; }
public int TourID { get; set; }
public DateTime date { get; set; }
public string purpose { get; set; }
}
var t = new List<Tour>
{
new Tour
{
accountid = 1,
TourID = 2,
date = DateTime.Now,
purpose = "Testing"
}
};
var output = t.Where(c => c.accountid == accId).Select(k => new
{
accountid = k.accountid,
TourID = k.TourID,
TourDetails = k.date.ToString() + k.purpose
}).ToList();
var o = JsonConvert.SerializeObject(output);
You can use something like this if you're in a MVC Controller requiring an ActionResult as output :
//here 'context' is your DbContext implementation and Tours is your DbSet.
var TourDetails= context.Tours.Where(t=>t.AccountID==_AccID)
.Select(s=>new {
AccountID = s.accountid,
TourID = s.TourID,
//s.fromdate.ToString("yyyyMMddHHmmss") use this to format your date if needed
TourDetails = s.fromdate.ToString() + s.purpose
})
.ToList();
//JsonRequestBehavior.AllowGet only if your not using POST method
return Json(TourDetails, JsonRequestBehavior.AllowGet);
I have following code in custom validation attribute called DateRange:
private DateTime _minDate = DateTime.Today.AddYears(-100);
private DateTime _maxDate = DateTime.MaxValue;
// String representation of the Min Date (yyyy/MM/dd)
public string Min
{
get { return FormatDate(_minDate, DateTime.Today.AddYears(-100)); }
set { _minDate = value == "Today" ? DateTime.Today : ParseDate(value, DateTime.Today.AddYears(-100)); }
}
// String representation of the Max Date (yyyy/MM/dd)
public string Max
{
get { return FormatDate(_maxDate, DateTime.MaxValue); }
set { _maxDate = value == "Today" ? DateTime.Today : ParseDate(value, DateTime.MaxValue); }
}
Then I write this attribute in metadata on some property of entity model like this:
[DateRange(Max = "Today")]
public string SomeDateProperty { get; set; };
I set breakpoint on Max property's getter. First time I open view, breakpoint is activated and DateTime.Today is got. Consequent refresh of the view does not activate breakpoint and old value is got. I think it's caching validation attribute. My question is: Is this because of caching? If it is, then how to disable it? Thanks in advance
The constructor for the custom attributes only get hit once, no idea how to turn off any sort of caching. The way I got round this for my scenario, was to only deal with the date calculation in the "IsValid" Method.
I created a date in the past attribute, that needed the date to be in the past, but you could set how long in the past was valid.
public class DateInPastAttribute : ValidationAttribute
{
private const string DefaultErrorMessage = "'{0}' must be in the past.";
public int DaysInPastAllowed { get; set; }
public DateInPastAttribute(int daysInPastAllowed)
: base(DefaultErrorMessage)
{
this.DaysInPastAllowed = daysInPastAllowed;
}
public override bool IsValid(object value)
{
if (!(value is DateTime))
{
return true;
}
DateTime maxDate = DateTime.Now;
DateTime minDate = maxDate.AddDays(this.DaysInPastAllowed * -1);
DateTime dateValue = (DateTime)value;
return
minDate <= dateValue &&
dateValue <= maxDate;
}
public override string FormatErrorMessage(string name)
{
return string.Format(CultureInfo.CurrentCulture, this.ErrorMessageString, name);
}
}
You can then use it in your view model like this:
[DateInPast(365)]
public DateTime DateReceived { get; set; }
Which would allow a date to be entered within the last year. You could amend this for the scenario that you require.
What do I want to achieve:
To show a seperate validation message for failing minimum age check and one for maximum age check
To store the minimum and maximum age in one area as integers. Not in js/ validator... only in the model. which I hope to change to look at a config file.
For the validation to work with jquery unobtrusive and server side, and to be in one place against the model (and obv some jquery)
To be enabled using data annotations
I wanted to check against DOB as a datetime, not have the user put in there age as an int. If i did I could have used [Min] notation and [Max] notation for age. It wasn't good enough.
Why didn't I use a range notation. I wanted to fire a different validation message for each min fail and max fail. I looked into this and had no look. I'd also have to pass in the range as a datetime and its static or something so I couldn't have done DateTime.Now.AddYears(-90) for instance.
My problems
I'm a noob at MVC, JQuery validation and the whole MVC architecture!
What I've come up with works. However, as you can see there is alot repeated code, I'd like to conform to DRY.
My first hack was to pass in the value that i'm checking against into the validation message. I got around this by doing...
[MaximumAgeCheck(90,"You have to be at most {0} to apply")]
and inside the validation attribute
private readonly int _min;
private readonly string _defaultErrorMessage = "";
public MinimumAgeCheck(int min, string defaultErrorMessage)
: base(defaultErrorMessage)
{
_min = min;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", _min.ToString());
}
and I used it for instance like so..
return new ValidationResult(_defaultErrorMessage);
I know this isn't the right way to do it, and wondered what is the best way to do this?
Second hack!
I'm passing in two validation parameters which I want to be able to access in the jQuery.validator.addMethod... method.
I tried to access these parameters by doing the following... params.[thevalueiadded], params[0]... etc, I even logged out params into console.log but it never showed me all the params, only the first value as a string!
My work around was to store the javascript variables at the top and load them from the adapters.add.
I'm probabily making little sense so here is the code, that works...I warn you, it is messy!
Model property and data annotation
[Required(ErrorMessage = "Date of birth required")]
[Display(Name = "Date of Birth")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
[DataType(DataType.DateTime, ErrorMessage = "Date of birth should be in dd/mm/yyyy format")]
[MinimumAgeCheck(18,"You have to be at least {0} to apply")]
[MaximumAgeCheck(90,"You have to be at most {0} to apply")]
public DateTime? DateOfBirth { get; set; }
Minimum Age Check and Maximum age check
validation attributes
public class MinimumAgeCheck : ValidationAttribute, IClientValidatable
{
private readonly int _min;
private readonly string _defaultErrorMessage = "";
public MinimumAgeCheck(int min, string defaultErrorMessage)
: base(defaultErrorMessage)
{
_min = min;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", _min.ToString());
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= _min && dtAge.Year <= 30))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString, _min);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule mcvrTwo = new ModelClientValidationRule();
mcvrTwo.ValidationType = "checkminimumage";
mcvrTwo.ErrorMessage = _defaultErrorMessage;
mcvrTwo.ValidationParameters.Add("todaysdate", DateTime.Now.ToString("dd/MM/yyyy"));
mcvrTwo.ValidationParameters.Add("lowerage", _min.ToString());
return new List<ModelClientValidationRule> { mcvrTwo };
}
}
public class MaximumAgeCheck : ValidationAttribute, IClientValidatable
{
private readonly int Max;
private readonly string _defaultErrorMessage = "";
public MaximumAgeCheck(int max, string defaultErrorMessage)
: base(defaultErrorMessage)
{
Max = max;
_defaultErrorMessage = defaultErrorMessage.Replace("{0}", Max.ToString());
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= Max && dtAge.Year <= 30))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,Max);
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
ModelClientValidationRule mcvrTwo = new ModelClientValidationRule();
mcvrTwo.ValidationType = "checkmaximumage";
mcvrTwo.ErrorMessage = _defaultErrorMessage;
mcvrTwo.ValidationParameters.Add("todaysdate", DateTime.Now.ToString("dd/MM/yyyy"));
mcvrTwo.ValidationParameters.Add("upperage", Max.ToString());
return new List<ModelClientValidationRule> { mcvrTwo };
}
}
The Jquery
(function ($) {
var mintodaysDateVal;
var maxtodaysDateVal;
var lowerageVal;
var upperageVal;
jQuery.validator.unobtrusive.adapters.add("checkminimumage", ['lowerage', 'todaysdate', 'upperage'], function (options) {
options.rules["checkminimumage"] = options.params;
mintodaysDateVal = options.params.todaysdate;
lowerageVal = options.params.lowerage;
options.messages["checkminimumage"] = options.message;
});
jQuery.validator.addMethod("checkminimumage", function (value, element, params) {
var currDate = mintodaysDateVal;
var sdoc = currDate.split('/');
var dobDate = value;
var sdob = dobDate.split('/');
//pass year,month,date in new Date object.
var vDOB = new Date(sdob[2], sdob[1] - 1, sdob[0]);
var vDOC = new Date(sdoc[2], sdoc[1] - 1, sdoc[0]);
//getAge user define function to calculate age.
var vYrs = getAge(vDOB, vDOC);
var result = false;
if (vYrs >= lowerageVal) { result = true; }
return result;
});
jQuery.validator.unobtrusive.adapters.add("checkmaximumage", ['lowerage', 'todaysdate', 'upperage'], function (options) {
options.rules["checkmaximumage"] = options.params;
maxtodaysDateVal = options.params.todaysdate;
upperageVal = options.params.upperage;
options.messages["checkmaximumage"] = options.message;
});
jQuery.validator.addMethod("checkmaximumage", function (value, element, params) {
var currDate = maxtodaysDateVal;
var sdoc = currDate.split('/');
var dobDate = value;
var sdob = dobDate.split('/');
var vDOB = new Date(sdob[2], sdob[1] - 1, sdob[0]);
var vDOC = new Date(sdoc[2], sdoc[1] - 1, sdoc[0]);
var vYrs = getAge(vDOB, vDOC);
var result = false;
if (vYrs <= upperageVal) { result = true; }
return result;
});
function getAge(oldDate, currDate) {
return currDate.getFullYear() - oldDate.getFullYear();
}
} (jQuery));
I hope this makes sense, I've read it over and its quite garbled... so i'll be happy to answer any comments.
Really useful code, but the ValidationResult IsValid method has some bugs. It doesn't handle future dates or blank dates. Plus it seems to have a hard coded limit to age 30 in - looks to be debug code? Anyway, I addressed those issues for my code and came up with the below:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return new ValidationResult(_defaultErrorMessage);
}
DateTime dtV = (DateTime)value;
long lTicks = DateTime.Now.Ticks - dtV.Ticks;
if (lTicks < 0)
{
return new ValidationResult(_defaultErrorMessage);
}
DateTime dtAge = new DateTime(lTicks);
if (!(dtAge.Year >= _min ))
{
return new ValidationResult(_defaultErrorMessage);
}
return ValidationResult.Success;
}
Take a look at the MVC Foolproof Validation library. You can find it in NuGet.
It has pretty much all the validation you need and is added via data annotations. It will intergrate nicely into the unobtrusive client side validation.