Data Annotation Ranges of Dates - c#

Is it possible to use [Range] annotation for dates?
something like
[Range(typeof(DateTime), DateTime.MinValue.ToString(), DateTime.Today.ToString())]

I did this to fix your problem
public class DateAttribute : RangeAttribute
{
public DateAttribute()
: base(typeof(DateTime), DateTime.Now.AddYears(-20).ToShortDateString(), DateTime.Now.AddYears(2).ToShortDateString()) { }
}

Docs on MSDN says you can use the RangeAttribute
[Range(typeof(DateTime), "1/2/2004", "3/4/2004",
ErrorMessage = "Value for {0} must be between {1} and {2}")]
public datetime Something { get; set;}

jQuery validation does not work with [Range(typeof(DateTime),"date1","date2"] --
My MSDN doc is incorrect

Here is another solution.
[Required(ErrorMessage = "Date Of Birth is Required")]
[DataType(DataType.Date, ErrorMessage ="Invalid Date Format")]
[Remote("IsValidDateOfBirth", "Validation", HttpMethod = "POST", ErrorMessage = "Please provide a valid date of birth.")]
[Display(Name ="Date of Birth")]
public DateTime DOB{ get; set; }
The simply create a new MVC controller called ValidationController and past this code in there. The nice thing about the "Remote" approach is you can leverage this framework to handle any kind of validations based on your custom logic.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Mail;
using System.Web;
using System.Web.Mvc;
namespace YOURNAMESPACEHERE
{
public class ValidationController : Controller
{
[HttpPost]
public JsonResult IsValidDateOfBirth(string dob)
{
var min = DateTime.Now.AddYears(-21);
var max = DateTime.Now.AddYears(-110);
var msg = string.Format("Please enter a value between {0:MM/dd/yyyy} and {1:MM/dd/yyyy}", max,min );
try
{
var date = DateTime.Parse(dob);
if(date > min || date < max)
return Json(msg);
else
return Json(true);
}
catch (Exception)
{
return Json(msg);
}
}
}
}

For those rare occurrences when you are forced to write a date as a string (when using attributes), I highly recommend using the ISO-8601 notation.
That eliminates any confusion as to whether 01/02/2004 is january 2nd or february 1st.
[Range(typeof(DateTime), "2004-12-01", "2004-12-31",
ErrorMessage = "Value for {0} must be between {1} and {2}")]
public datetime Something { get; set;}

I use this approach:
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
internal sealed class DateRangeAttribute : ValidationAttribute
{
public DateTime Minimum { get; }
public DateTime Maximum { get; }
public DateRangeAttribute(string minimum = null, string maximum = null, string format = null)
{
format = format ?? #"yyyy-MM-dd'T'HH:mm:ss.FFFK"; //iso8601
Minimum = minimum == null ? DateTime.MinValue : DateTime.ParseExact(minimum, new[] { format }, CultureInfo.InvariantCulture, DateTimeStyles.None); //0 invariantculture
Maximum = maximum == null ? DateTime.MaxValue : DateTime.ParseExact(maximum, new[] { format }, CultureInfo.InvariantCulture, DateTimeStyles.None); //0 invariantculture
if (Minimum > Maximum)
throw new InvalidOperationException($"Specified max-date '{maximum}' is less than the specified min-date '{minimum}'");
}
//0 the sole reason for employing this custom validator instead of the mere rangevalidator is that we wanted to apply invariantculture to the parsing instead of
// using currentculture like the range attribute does this is immensely important in order for us to be able to dodge nasty hiccups in production environments
public override bool IsValid(object value)
{
if (value == null) //0 null
return true;
var s = value as string;
if (s != null && string.IsNullOrEmpty(s)) //0 null
return true;
var min = (IComparable)Minimum;
var max = (IComparable)Maximum;
return min.CompareTo(value) <= 0 && max.CompareTo(value) >= 0;
}
//0 null values should be handled with the required attribute
public override string FormatErrorMessage(string name) => string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, Minimum, Maximum);
}
And use it like so:
[DateRange("2004-12-01", "2004-12-2", "yyyy-M-d")]
ErrorMessage = "Value for {0} must be between {1} and {2}")]

I found issues with the [Range(typeof(DateTime)] annotation and would describe it as "clunky at best" it leaves too much to chance IF it works.
Remote validation seems to be a good way of: avoiding javascript in views and maintaining server side code integrity, personally never like sending code to a client to execute if I can avoid it.
Using #StackThis answer as a base and reference to an article on remote validation in MVC3
Model
public class SomeDateModel
{
public int MinYears = 18;
public int MaxYears = 110;
[Display(Name = "Date of birth", Prompt = "e.g. 01/01/1900")]
[Remote(action: "ValidateDateBetweenYearsFromNow", controller: "Validation", areaReference: AreaReference.UseRoot, AdditionalFields = "MinYears,MaxYears", HttpMethod = "GET" ,ErrorMessage = "Subject must be over 18")]
public DateTime? DOB { get; set; }
}
Controller - Deployed at the root directory
namespace Controllers
{
public class ValidationController : Controller
{
[HttpGet]
[ActionName("ValidateDateBetweenYearsFromNow")]
public JsonResult ValidateDateBetweenYearsFromNow_Get()
{
//This method expects 3 parameters, they're anonymously declared through the Request Querystring,
//Ensure the order of params is:
//[0] DateTime
//[1] Int Minmum Years Ago e.g. for 18 years from today this would be 18
//[2] int Maximum Years Ago e.g. for 100 years from today this would be 100
var msg = string.Format("An error occured checking the Date field validity");
try
{
int MinYears = int.Parse(Request.QueryString[1]);
int MaxYears = int.Parse(Request.QueryString[2]);
//Use (0 - x) to invert the positive int to a negative.
var min = DateTime.Now.AddYears((0-MinYears));
var max = DateTime.Now.AddYears((0-MaxYears));
//reset the response error msg now all parsing and assignmenst succeeded.
msg = string.Format("Please enter a value between {0:dd/MM/yyyy} and {1:dd/MM/yyyy}", max, min);
var date = DateTime.Parse(Request.QueryString[0]);
if (date > min || date < max)
//switch the return value here from "msg" to "false" as a bool to use the MODEL error message
return Json(msg, JsonRequestBehavior.AllowGet);
else
return Json(true, JsonRequestBehavior.AllowGet);
}
catch (Exception)
{
return Json(msg, JsonRequestBehavior.AllowGet);
}
}
}
}
The msg variable is displayed as part of the Html helper ValidationSummary or the Html helper ValidationFor(x=>x.DATETIME)
View
It's important to note that the fields passed as parameter 2 and 3 must exist in the view in order for the remote validation to pass the values to the controller:
#Html.EditorFor(m => m.DOB)
#Html.HiddenFor(m => m.MinYears)
#Html.HiddenFor(m => m.MaxYears)
#Html.ValidationSummary()
The model and Html helpers will do all the jquery work for you.

Related

Swedish SSN Regular expression reject users under a specific age

I Have a problem with my regular expression. I have made it possible to valdiate correct swedish social security number to match these criteria.
YYMMDDNNNN
YYMMDD-NNNN
YYYYMMDDNNNN
YYYYMMDD-NNNN
But i would also like to reject a user to register if the user is under 18 years old. My reqular expression is looking like this at the moment: Do anyone encountered the same problem with the age range Swedish SSN?
private const string RegExForValidation =
#"^(\d{6}|\d{8})[-|(\s)]{0,1}\d{4}$";
UPDATE
private const string RegExForValidation = #"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
string date = Regex.Match("19970918-1234", RegExForValidation).Groups["date"].Value;
DateTime dt;
[Required(ErrorMessage = "Du måste ange personnummer")]
[RegularExpression(RegExForValidation, ErrorMessage = "Personnummer anges med 10 siffror (yymmddnnnn)")]
public string PersonalIdentityNumber { get; set; }
Second Update
public class ValidateOfAge : ValidationAttribute
{
public bool IsOfAge(DateTime birthdate)
{
DateTime today = DateTime.Today;
int age = today.Year - 18;
if (birthdate.AddYears(birthdate.Year) < today.AddYears(-age))
return false;
else
return true;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string RegExForValidation = #"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
string date = Regex.Match((string)value, RegExForValidation).Groups["date"].Value;
DateTime dt;
if (DateTime.TryParseExact(date, new[] { "yyMMdd", "yyyyMMdd" }, CultureInfo.InvariantCulture, DateTimeStyles.None, out dt))
if (IsOfAge(dt))
return ValidationResult.Success;
return new ValidationResult("Personnummer anges med 10 siffror (yymmddnnnn)");
}
}
This is a case where I wouldn't use regular expressions, but instead rely on the base class library's built-in DateTime parsing functionality:
public DateTime GetBirthDate(string ssn)
{
var strippedOfSerial =
ssn.Substring(0, ssn.Length - 4).TrimEnd('-');
return DateTime.ParseExact(
strippedOfSerial,
new[] { "yyMMdd", "yyyyMMdd" },
new CultureInfo("se-SE"),
DateTimeStyles.None);
}
Now you can look at the returned DateTime value and compare it to DateTime.Now in order to figure out if you want to reject it or not.
In my opinion, this is much more readable than relying on regular expressions, and may be safer and more flexible as well. As you can tell, you can e.g. use other cultures etc. to tweak the parsing strategy.
You need to get the birthdate from the SSN, parse to DateTime, and then compare with today's date.
Here is the method checking if a person is of age:
public bool IsOfAge(DateTime birthdate)
{
DateTime today = DateTime.Today; // Calculating age...
int age = today.Year - birthdate.Year;
if (birthdate > today.AddYears(-age))
age--;
return age < 18 ? false : true; // If the age is 18+ > true, else false.
}
And here is how you can use this:
string RegExForValidation = #"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
string date = Regex.Match("19970918-1234", RegExForValidation).Groups["date"].Value;
DateTime dt;
if (DateTime.TryParseExact(date, new[] { "yyMMdd", "yyyyMMdd" }, new CultureInfo("sv-SE"), DateTimeStyles.None, out dt))
Console.WriteLine(IsOfAge(dt));
Note that [-|(\s)] matches a -, |, (, whitespace or ). I am sure you only want to match a hyphen or whitespace.
I added a named capture to the regex and removed unnecessary symbols from the character class. Also, note that {0,1} is the same as ?.
UPDATE
To make it work in an MVC app, you need to implement a custom validator:
[Required(ErrorMessage = "Du måste ange personnummer")]
[ValidateOfAge] // <---------------------------- HERE
public string PersonalIdentityNumber { get; set; }
And implement this as follows:
public class ValidateOfAge: ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
string RegExForValidation = #"^(?<date>\d{6}|\d{8})[-\s]?\d{4}$";
string date = Regex.Match((string)value, RegExForValidation).Groups["date"].Value;
DateTime dt;
if (DateTime.TryParseExact(date, new[] { "yyMMdd", "yyyyMMdd" }, new CultureInfo("sv-SE"), DateTimeStyles.None, out dt))
if (IsOfAge(dt))
return ValidationResult.Success;
return new ValidationResult("Personnummer anges med 10 siffror (yymmddnnnn)");
}
}
Instead of doing custom RegEx, I'd suggest looking at a NuGet package available that is able to parse a valid Swedish Personal Identity Number and then extract information such as DateOfBirth, Age, Gender etc that could be used as input to your validation. The method .GetAge() would be the most relevant in your case. Disclaimer: I'm one of the co-authors of that package.
An implementation of ValidateOfAge using SwedishPersonalIdentityNumber could look like:
public class ValidateOfAge : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (SwedishPersonalIdentityNumber.TryParse((string)value, out var personalIdentityNumber))
{
if(personalIdentityNumber.GetAge() >= 18) {
return ValidationResult.Success;
} else {
return new ValidationResult("Du måste vara minst 18 år gammal.");
}
}
return new ValidationResult("Ogiltigt personnummer.");
}
}
SwedishPersonalIdentityNumber supports the patterns you describe, including a few more that is valid (did you know that a PIN can contain a + for example?).
You can find the source code here:
https://github.com/ActiveLogin/ActiveLogin.Identity
And the packages on NuGet here:
https://www.nuget.org/packages/ActiveLogin.Identity.Swedish/
https://www.nuget.org/packages/ActiveLogin.Identity.Swedish.AspNetCore/
The second one contains a validation attribute for validating a PIN in your model.
If the use case of validating age is common enough, we could implement it into that validation attribute. I created an issue to find out about interest and the design.
The API design proposed at the moment is this (for your scenario):
[SwedishPersonalIdentityNumber(MinimumAge = 18)]
public string PersonalIdentityNumber { get; set; }
Please share your thoughts in the issue if this is something you'd like to see implemented.

mvc-model date range annotation dynamic dates

with reference to this thread in stackoverflow
[Range(typeof(DateTime), "1/2/2004", "3/4/2004",
ErrorMessage = "Value for {0} must be between {1} and {2}")]
public DateTime EventOccurDate{get;set;}
I tried to add some dynamic dates into my model's date range validator as:
private string currdate=DateTime.Now.ToString();
private string futuredate=DateTime.Now.AddMonths(6).ToString();
[Range(typeof(DateTime),currdate,futuredate,
ErrorMessage = "Value for {0} must be between {1} and {2}")]
public DateTime EventOccurDate{get;set;}
But Error Occurs.Is there no way to set dynamic date range validation in MVC?
You cannot use dynamic values in attributes because they are metadata that is generated at compile-time. One possibility to achieve this is to write a custom validation attribute or use Fluent Validation which allows for expressing more complex validation scenarios using a fluent expressions.
Here's an example of how such custom validation attribute might look like:
public class MyValidationAttribute: ValidationAttribute
{
public MyValidationAttribute(int monthsSpan)
{
this.MonthsSpan = monthsSpan;
}
public int MonthsSpan { get; private set; }
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null)
{
var date = (DateTime)value;
var now = DateTime.Now;
var futureDate = now.AddMonths(this.MonthsSpan);
if (now <= date && date < futureDate)
{
return null;
}
}
return new ValidationResult(this.FormatErrorMessage(this.ErrorMessage));
}
}
and then decorate your model with it:
[MyValidation(6)]
public DateTime EventOccurDate { get; set; }

How create a dynamic DataAnnotations Attribute for validation on MVC5 (globalization)?

I'm using ef6 with mvc5.
My project need multiple language. So I need create a DataAnnotations Attribute for validation these field.
For example: I have a Id property:
public int Id { get; set; }
For validation I need add annotations like
[Display("User Id")]
Required(ErrorMessage = "Please input the Id")
public int Id { get; set; }
But I need use multiple language , So I create a new DataAnnotations Attribute(https://stackoverflow.com/a/2432520/1900498):
public class LocalizedDisplayAttribute : DisplayNameAttribute
{
public LocalizedDisplayAttribute(string resourceId)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceId)
{
// TODO: Return the string from the resource file
}
}
It works fine , but it will cache the result, then when session changed(I'm use session save the user website page language mode. like 1 mean English, 0 mean other language), it still not change, this is a problem for me.
second problem is: I don't know how to rewrite RequiredAttribute for let user know , some field can't empty.
but I also find there is another problem , looks like I need to rewrite the message about numeric field......(xx field must be numeric)
So Is there any way can rewrite the validation rule, let me decide the error message for Required, Range, Numeric...... and it will cache it but when session changed, it will read again?
For example:
// if session changed rewrite rule message for current language
if (session["language"].ToString() != LastLanguage)
{
if (session["language"].ToString() == "1")
//English
{
RequiredMessage = "the field {0} must be required";
NumericMessage = "the field {0} must be a number";
LastLanguage = 1;
} else{
// other language
RequiredMessage = "xx xx {0} xxxxxxxxx";
NumericMessage = "xx xx {0} xxxxxxxxxx";
LastLanguage = 0;
}
}
Of course, not only the validation message, I need globalization the field display name too.
DataAnnotation already provides globalization support:
[Display(ResourceType = typeof(Resource), Name = "Test")]
[Required(ErrorMessageResourceType = typeof(Resource), ErrorMessageResourceName = "TestRequired")]
public string Test { get; set; }
To change the current culture in the global.asax
private void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Context != null && Context.Session != null)
{
string language = Context.Session["language"] as string;
if (language != null)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(language);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);
}
}
}

Money edit box with 2 decimal places

MVC4, Code First, C# project
When populating a money field with either a explicit value or from a table read the TextBoxFor displays the value with 2 decimal places. If the field is populated from a money field in another class it displays 4 decimal places.
public class Class1
{
[Column(TypeName = "money")]
public decimal Field1 { get; set; }
}
public class Class2
{
[Column(TypeName = "money")]
public decimal Field1 { get; set; }
}
public class Table1
{
public int Id { get; set; } public decimal Value { get; set; }
}
Scenario 1:
Class1.Field1 = 14.95M;
Scenario 2:
Class2.Field1 = Table1.Value;
Scenario 3:
Class1.Field1 = Class2.Field1
View
#Html.TextBoxFor(m => m.Class1.Field1, new { style = "width:70px;" })
For Scenario 1 & 2 the TextBoxFor correctly displays 2 decimal places, with Scenario 3 it displays 4 decimal places in the edit box. I need to use TextBoxFor so I can pass html attributes.
The instance of Class2 is itself pre-populated from values in a Table generated by Class2. I've examined everything with SSMS [all the applicable fields in the tables are (money, not null)] and in debug and cannot find any discrepancies.
Why does the TextBoxFor incorrectly display the money format for Scenario 3 (I understand that SQL stores decimals with 4 decimal precision)?
More importantly how do I get my edit box to always display money values with 2 decimals?
In my MVC app I wanted a text box to display like $4.95. I used editor template.
#if(Model != null && Model.GetType() == typeof(string))
{
#Html.TextBox(
"",
string.Format("{0:c}", (decimal)decimal.Parse(Model))
)
}
#if(Model != null && Model.GetType() == typeof(decimal))
{
#Html.TextBox(
"",
string.Format("{0:c}", (decimal) Model),new {#class="no-number-validation"}
)
}
#if(Model == null)
{
#Html.TextBox("",null,new {#class="no-number-validation"})
}
And then obviously I wanted to be able to send back $4.95 to the server and have the Model Binder still handle it automatically. This example handles % signs too.
public class DecimalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
if (valueResult.AttemptedValue.StartsWith("$"))
{
actualValue = decimal.Parse(valueResult.AttemptedValue, NumberStyles.Currency);
}
if (valueResult.AttemptedValue.EndsWith("%"))
{
actualValue = decimal.Parse(valueResult.AttemptedValue.Replace("%", "").Trim(),
CultureInfo.CurrentCulture);
}
if (actualValue == null)
actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
CultureInfo.CurrentCulture);
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
This is nice because I don't have to use a string as my property type for the currency textbox in order to handle the $ sign. By the time the object gets populated there is no money sign and the value gets assigned right into the decimal type.
You can round to 2 decimals (original values are 2 decimal precision so I'm not worrying about rounding errors).
decimal.Round(Value, 2)
Class1.Field1 = decimal.Round(Class2.Field1,2)
It can then be implemented through an extension method.
public static decimal dR2(this decimal ip) { return decimal.Round(ip, 2); }
Class1.Field1 = Class2.Field1.dR2();

Regular Expression stop crashing

Consider this code:
[Required]
[RegularExpression(#"\d{2,2}/\d{2,2}/\d{4,4} \d{2,2}:\d{2,2}:\d{2,2}",
ErrorMessage = "Wrong Syntax Entered, Needed:day/Month/Year Hour:Minutes:Seconds")]
public DateTime Posted { get; set; }
When I enter this value, my application crashes: 00/00/0000 00:00:00
Is there a way to stop this and make it more realistic? I'm wanting to allow the date so it only allows max 31 days or less up 1 and allows max months up 12 and min 1?
A regex is the wrong way to validate dates and times. Use DateTime.TryParse instead.
EDIT:
Here's an example:
using System;
using System.Globalization;
...
bool valid;
DateTime dt;
valid = DateTime.TryParseExact(inputString, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out dt);
I agree with #MRAB that TryParse is probably easier to manage and maintain.
This SO question also tries to do what you're doing and they created a custom attribute (that derives from RegularExpressionAttribute) that seems to have solved his problem. Maybe it will help you.
Hope this helps.
Yet another, change FormatException message in ModelBinder(but going to far...).
DefaultModelBinder.GetValueInvalidResource is static method.I can not override this method. Because I create CustomModelBinder class and override SetProperty method.
[Required]
[AdditionalMetadata(
"PropertyValueInvalid",
"Wrong Syntax Entered, Needed:day/Month/Year Hour:Minutes:Seconds")]
public DateTime? Posted { get; set; }
And create custom ModelBinder
public class CustomModelBinder : DefaultModelBinder
{
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
{
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
var propertyMetadata = bindingContext.PropertyMetadata[propertyDescriptor.Name];
var invalidMessage = propertyMetadata.AdditionalValues.ContainsKey("PropertyValueInvalid")
? (string)propertyMetadata.AdditionalValues["PropertyValueInvalid"]
: string.Empty;
if (string.IsNullOrEmpty(invalidMessage))
{
return;
}
// code from DefaultModelBinder
string fullPropertyKey = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
if (!bindingContext.ValueProvider.ContainsPrefix(fullPropertyKey))
{
return;
}
ModelState modelState = bindingContext.ModelState[fullPropertyKey];
foreach (ModelError error in modelState.Errors.Where(err => String.IsNullOrEmpty(err.ErrorMessage) && err.Exception != null).ToList())
{
for (Exception exception = error.Exception; exception != null; exception = exception.InnerException)
{
if (exception is FormatException)
{
string displayName = propertyMetadata.GetDisplayName();
string errorMessageTemplate = invalidMessage;
string errorMessage = String.Format(CultureInfo.CurrentCulture, errorMessageTemplate,
modelState.Value.AttemptedValue, displayName);
modelState.Errors.Remove(error);
modelState.Errors.Add(errorMessage);
break;
}
}
}
}
}
How about this?
Validating datatime with Regex will result in complex regex like
^([0][1-9]||[1-2][0-9]||[3][0-1])/([0][1-9]||[1][1-2])/([1][0-9]{3}) ([0][1-9]||[1][0-2]):([0][1-9]||[1-5][0-9]):([0][1-9]||[1-5][0-9])$
But Still i doubt it may miss some edge cases.
If you are using MVC3, then the best way to Use Self validating model like follows,
public class TestModel:IValidatableObject
{
string MyDateTime{get;set;}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> v = new List<ValidationResult>();
DateTime dt = default(DateTime);
DateTime.TryParseExact(MyDateTime, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture,DateTimeStyles.None,out dt);
if (dt.Equals(default(DateTime)))
v.Add(new ValidationResult("Invalid Date time"));
return v;
}
}

Categories

Resources