How to compare dates independent of current culture in .NET - c#-2.0

I hardcode a trial expiration date in my .net 2.5 app. how do I compare it with the user's system date such that the comparison is accurate regardless of the user's culture settings?
DateTime maxTrialDate = DateTime.Parse("11/17/2020", new System.Globalization.CultureInfo("en-US"));
DateTime curDate = DateTime.Parse(DateTime.Now.ToShortDateString(), new System.Globalization.CultureInfo("en-US"));
//the next line of code uses the DateDiff method to compare the two dates -dont recall its //exact syntax.
On my XP machine the above works if the control panel regional setting for datetime is en-US, but if I change it to en-AU, then the above code that sets curDate fires a FormatException "Date is not in a correct string format"

If you avoid using strings to represent the dates, you will not encounter this problem:
DateTime maxTrialDate = new DateTime(2020, 11, 17);
if (DateTime.Now.Date > maxTrialDate)
{
// expired
}
The DateTime is created by explicitly defining the day, month and year components, so the regional settings will not confuse matters.

What about just using CultureInfo.InvariantCulture all over the place?

You can use System.Globalization.CultureInfo.InvariantCulture

If I remember correctly, in most places outside the US, the standard date format is dd/mm/yyyy, rather than the US standard of mm/dd/yyyy. It might be that when trying to parse the date, it believes the 17 is the month, which is an invalid month, thus causing the error.

Why are you using the Parse method if you are hardcoding expiration date just compare it to
DateTime.now

The FormatException is expected since you explicitly ask the parser to use en-US.
Try calling the one-argument overload of DateTime.Parse(), or alternatively, if you really want to use the two-args overload (*cough*FxCop*cough*), something like:
using System.Globalization;
DateTime.Parse("11/17/2020", CultureInfo.CurrentCulture);

Related

Vaadin: How to change a date format?

I am pulling a Date from a database, reassigning it as a TextField, and then toString() it, but the date comes out "Thurs 0900 OCT 12 2015" when all I need is MM/DD/YYYY format. How do I change the format?
Date myDatabaseDate = someDBGetMethod();
TextField myDateTF = new TextField()
myDateTF.setCaption("My date is: ");
myDateTF.setValue(myDatabaseDate).toString());
myDateTF.setReadOnly(true);
FormLayout fLayout = new FormLayout();
addComponent(fLayout);
fLayout.addComponent(myDateTF);
What's happening: Thu Oct 22 12:19:04 CDT 2015
What I want: 22/10/2015
Thank you in advance!
Examples make the most sense to me as I am very new to vaadin.
Vaadin Not The Problem
If trying to display a String representation of a date-time object, then Vaadin is not a part of the problem. Vaadin offers a widget, DateField, for the user to pick a date-time value. You do not need that widget for simply displaying the string representation. For display, use a TextField as you described in the Question.
So the problem is how to generate that String representation.
First be aware that Java includes two classes named "Date":
java.util.Date
java.sql.Date
Confusingly, they are not parallel. The util one is a date plus a time-of-day in UTC. The sql one is meant to represent a date-only, but is poorly designed; as a hack it subclasses the util one but alters the time-of-day to be 00:00:00.000. These old date-time classes in early Java are a bad mess.
From a database you should be receiving the latter, java.sql.Date for a date-only stored value. If you were storing date-time values, then you should be getting java.sql.Timestamp objects from your JDBC driver.
java.time LocalDate
As java.sql.Date is one of the old clunky date-time classes bundled with early Java, we should convert to the new classes found in the java.time framework built into Java 8 and later. See Tutorial.
For a date-only value, java.time offers the LocalDate class. The "Local" in the name refers to any locality rather than a particular locality. It represents the vague idea of a date, but is not tied to the timeline. A date starts earlier in Paris than in Montréal, for example. If you care about exact moments on the timeline, you would be using java.sql.Timestamp rather than java.sql.Date.
Converting from java.sql.Date to java.time.LocalDate is an easy one-liner, as a conversion method is provided for you.
LocalDate localDate = myJavaSqlDate.toLocalDate();
Going the other way is just as easy, when storing data into the database.
java.sql.Date myJavaSqlDate = java.sql.Date.valueOf( localDate );
With a LocalDate in hand, you can call on the java.time.format package to format a string localized for easy reading by the user. Specify the Locale expected by the user.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH );
String output = localDate.format( formatter );
Example output:
dimanche 1 novembre 2015
ZonedDateTime
Let’s consider if you did get a java.sql.Timestamp rather than java.sql.Date.
We can convert from a java.sql.Timestamp to a Instant, a moment on the timeline in UTC.
Instant instant = myTimestamp.toInstant();
Next, assign a time zone.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId);
We use the java.time.format package to create the String representation. Note the chained calls, the second one setting a specific Locale. If not specified, your JVM’s current default Locale is implicitly applied. Better to be explicit.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.FULL ).withLocale ( Locale.CANADA_FRENCH );
String output = zdt.format ( formatter );
Example output:
dimanche 1 novembre 2015 2 h 24 EST
java.util.date object does not contain any information how to present himself as String. That's because in different locations communities writes dates in different ways. So Java separates operations on dates from printing them out on the screen.
What you are missing in your code is a DateFormat.
Date myDatabaseDate = new Date(2014,10,10);
DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, Locale.UK);
TextField myDateTF = new TextField();
myDateTF.setCaption("My date is: ");
myDateTF.setValue(format.format(myDatabaseDate));
myDateTF.setReadOnly(true);
layout.addComponent(myDateTF);
I would strongly encourage you however to use Java 8 Dates API since Java 7 Dates API is a total mess. You can read more about that here What's wrong with Java Date & Time API?

OData: Date "Greater Than" filter

Is there a way to return a series of records in OData by specifying a "Date greater than xxxxx" filter...but using a Date that was previously obtained form an OData feed?
Use Case: Pretend that I want to build a web page that displays a list of the most recently completed online orders. This is what I'm aiming for:
Load the page
Hit my OData service asynchronously, returning the last 100 orders (ordering by date descending so that the most recently completed order shows up first)
Build the HTML on the page using the OData data
Store the MAX date into a global variable (looks like this: /Date(1338336000000)/)
Hit the OData service on a 30 second interval but this time specify a filter to only return records where the order date is greater than the previous MAX Date. In this case: /Date(1338336000000)/
If any records are returned, build the HTML for those records and prepend the items to the previously loaded items.
Where I am struggling is in specifying the Date "greater than" filter. For some reason, the date filters in OData do not seem to play very nice with OData's own native date format. Do I need to convert the date originally obtained into a different format that can be used for filtering?
I want to do something like this:
http://mydomain/Services/v001.svc/Orders?$filter=close_dt%20gt%201338336000000
FYI: I'm using V2
Figured this out.
OData V2 out-of-the-box returns dates out of SQL in JSON Date format like so:
/Date(1338282808000)/
However, in order to use a date as a filter within an OData call, your date has to be in EDM format, looking like this:
2012-05-29T09:13:28
So, I needed to get the date from my initial OData call, then convert it to the EDM format for use in my subsequent OData calls, which look like this:
/Services/v001.svc/Orders?$filter=close_dt gt DateTime'2012-05-29T09:13:28'
I ended up creating a javascript function that does the formatting switcharoo:
function convertJSONDate(jsonDate, returnFormat) {
var myDate = new Date(jsonDate.match(/\d+/)[0] * 1);
myDate.add(4).hours(); //using {date.format.js} to add time to compensate for timezone offset
return myDate.format(returnFormat); //using {date.format.js} plugin to format :: EDM FORMAT='yyyy-MM-ddTHH:mm:ss'
}
A couple of notes:
The JSON format does not seem to adjust for timezone, so the date returned does not match the date I see in my database. So I had to add time manually to compensate (someone please explain this).
I am using the date.format.js plugin which you can download here for formatting the date and adding time.
In OData V4 date filtering format has changed to $filter=close_dt gt 2006-12-30T23:59:59.99Z
For example
http://services.odata.org/V4/OData/OData.svc/Products?$filter=ReleaseDate%20gt%202006-12-30T23:59:59.99Z
For previous versions of OData see previous answers
If you use the datetime logic, you can do lt or gt.
e.g.
...mydomain/Services/v001.svc/Orders?$filter=close_dt gt datetime'20141231'
Just an FYI: in V3 of the protocol the non-tick-based datetime format is now the default:
http://services.odata.org/Experimental/OData/OData.svc/Products%280%29?$format=application/json;odata=verbose&$select=ReleaseDate
..."ReleaseDate":"1992-01-01T00:00:00"...
I will just try to make the answer of #avitenberg more clear:
var date= DateTime.Now;
//Convert time to UTC format
$"filter={close_dt} gt {date:yyyy-MM-ddTHH:mm:ss.FFFZ}";
See Microsoft:

conversion error from string, when using params in SQL

using Delphi 2010 (Firebird [testing], MS Sql Server, Oracle [production])
The following is my SQL
SELECT p.script_no, MIN(p.start_Time) as startTime, MAX(p.end_Time) as endTime,
SUM(p.duration) as TotalDuration
FROM phase_times p
WHERE (p.script_no=:scriptNo) AND (Trunc(p.start_time) >= :beginDateRange) AND (Trunc(p.start_time) <= :endDateRange)
GROUP BY p.script_no
ParamByName('beginDateRange').AsDate:= Date - 30;
ParamByName('endDateRange').AsDate:= Date;
I am getting a "conversion error from string - 10-25-2012" and i am not sure why, since my datetime fields are in the "10/25/2012 9:20:49 AM" format in the database.
If i change it to the following : ParamByName('beginDateRange').AsString := formatDateTime('mm/dd/yyyy',Date - 30).....i get the error "conversion error from string - 10/25/2012"
reserching this error has provided me no new avenues, do you have any ideas?
According to the Interbase 6.0 manual, Embedded SQL guide, chapter 7, Firebird supports conversion from YYYY-MM-DD and YYYY-MM-DD HH:MM:SS.qqq. I also believe it supports American style shorthand dates (eg 1-JAN-2012) for conversion.
It may be there are some locale dependent conversion supported, but in general: use an actual date/timestamp type instead of a string.
UPDATE
I initially did not spot the TRUNC in your query: it is the cause of the conversion error as this function only works on numbers, see the manual entry for TRUNC.
Given your comment (and the respons of ain) I assume you are only interested in the date part, and want to ignore the time. If so, rewrite your use of TRUNC to:
CAST(<your-timestamp-field> AS DATE)
And the condition (Trunc(p.start_time) >= :beginDateRange) AND (Trunc(p.start_time) <= :endDateRange) to:
CAST(p.start_time AS DATE) BETWEEN :beginDateRange AND :endDateRange
Firebird doesn't support conversion from string to date and time value if string is in 12 hour format. Use 'dd.mm.yyyy hh:mm:ss' or 'mm/dd/yyyy hh:mm:ss' formats.
String to date/time conversions usually use user's locale.
So if you feed a date/time conversion function with string that does not match your date part of the locale - you will get similar errors.
Also specifying date/time values like "10/25/2012" is Locale dependent. So if you execute your program on a computer with different than US Locale (like Mine) - it's likely to fail if using "10/25/2012".
To be Locale independent I suggest two options:
Use StrToDateTime, specifying TFormatSettings
Use ISO 8601 for specifying date/time strings (but I don't think Delphi supports that...)
BTW programs like MS Sql, Excel etc. accept dates in ISO 8601. But you have to check this for FB.
Regarding this:
...since my datetime fields are in the "10/25/2012 9:20:49 AM" format in the database...
The internal storage of date/time fields varies between different DB Engines. What you see in your DB Management Software ("10/25/2012 9:20:49 AM" in your case) is the string representation of the data field, usually formatted (again) according your user Locale
if you connected with DB from Firebird 1.0 under the server Firebird 2.1 (for example) you need todo backup and restore under Firebird 2.1

How do I pass a date via the URL, for my Action to read in MVC?

How do I pass a date via the URL?
Here is what I am trying to do, as as you'll see the date uses forward slashes which are invalid in the URL
http://localhost/Controller/Action/id=0d5375aa-6d43-42f1-91f0-ea73d9beb361&date=02/12/2009
The ISO 8601 standard is yyyy-MM-dd, which is unambiguous and does not contain any invalid URL characters, and works fine in DateTime.Parse/TryParse.
Another option is to use whatever format you want and simply encode the URL using HttpServerUtility.UrlEncode/UrlDecode.
You could pass a date in the query string using a specific format, say yyyymmdd and then parse it correctly in your Controller.
&date=02/12/2009
change to
&date=20091202 (yyyymmdd)
You could either create a wrapper around the DateTime object that was instantiated using this new format or just parse it yourself in the Controller.
public MyWrapperDate(int date)
{
int year = date / 10000;
int month = ((date - (10000 * year)) / 100);
int day = (date - (10000 * year) - (100 * month));
this.DateTimeObject = new DateTime(year, month, day);
}
You could URL encode it, but passing a DateTime around as a string is always a bit tricky because you may run into parsing errors if the request ever crosses culture boundaries.
A better option is to convert the DateTime to Ticks and pass that number around.
MVC uses the current culture when generating URL and binding Models. It makes sense in some scenarios (like when you have a textbox and the user enters the date there) but there are often problems. If you have different cultures then it is easier if values are always in the format for invariant culture.
In your case I would pass the value as a string rendered by invariant culture. Then I would use a CustomModelBinder to fill the property in the Model.

I'm getting "Invalid month in date" trying to run this?

I'm trying to run the following db command against Informix:
delete from table1
where u_id in (select u_id
from table2
where c_id in (select c_id
from ptable
where name = 'Smith'
and dob = '29-08-1946'));
I pass this in as a string to the db.ExecuteNonQuery method in the MS Data Application block and I get the above error?
To get the date format '29-08-1946' to work, you need your DBDATE environment variable set to a value such as "DMY4-" (or "DMY4/"). These are standard variations for the UK (I used them for years; I now use "Y4MD-" exclusively, which matches both ISO 8601:2004 (Date formats) and ISO 9075 (SQL), except when debugging someone else's environment). There are other environment variables that can affect date formatting - quite a lot of them, in fact - but DBDATE takes priority over the others, so it is the big sledgehammer that fixes the problem.
One of the problems is that your notation using a plain string is not portable between US and UK (and ISO) settings of DBDATE. If you have a choice, the neutral constructor for dates is the MDY() function:
WHERE dob = MDY(8,29,1946)
This works regardless of the setting of DBDATE. You can probably use TO_DATE() too:
SELECT TO_DATE('29-08-1946', '%d-%m-%Y') FROM dual;
This generated '1946-08-29 00:00:00.00000' for me - the function generates a DATETIME YEAR TO FRACTION(5) value, but those convert reliably to DATE values in Informix.
You can also use the DATE() function or an explicit cast to DATE (either CAST('29-08-1946' AS DATE) or '29-08-1946'::DATE), but both of those are subject to the whims of the locale of the users.
Your date field is improperly formatted. Since there is no 29th month in the year 1946 that is what is causing the error.
I'd try just swapping the month and day. 08-29-1946.
The way the day and month parts of a date string are read in can depend on your computer's culture settings.
It is always safer to pass date strings to a database in the form 'dd-MMM-yyyy' (i.e. '29-aug-1946')
It's even safer to pass them as YYYY-MM-DD, the dd-MMM-yyyy in that example will fail on a server with a (for example) French locale.

Resources