Comparing date time - language-agnostic

It seems I can't compare 2 date values with time.
When I compare just date part - year, month and day - everything works fine. But when I add comparing times everything breaks down and not even month fits.
So what's some basic algorithm for comparing 2 dates? I mean generally, I can't juse use dateTime1.CompareTo(dateTime2). Preferably in pseudocoude. Thanks.
Edit: Now I found out that it only compares with last comparision. So if last is day comparing it is compared by days and doesn't take into account year or month.
I just need basic algorithm for comparing 2 dates. I believe it must be simple...

The following format should work pretty well:
DateTime a
DateTime b
if a.year != b.year
return a.year < b.year
else if a.month != b.month
return a.month < b.month
else if a.day != b.day
return a.day < b.day
else if a.hour != b.hour
return a.hour < b.hour
else if a.minute != b.minute
return a.minute < b.minute
else
// Dates are same

Compare the years. If one is bigger than the other, that's the later date.
Compare the months. If one is bigger than the other, that's the later date.
Compare the days. If one is bigger than the other, that's the later date.
Etcetera. I think the pattern is clear.
Pseudocode (and valid Python, incidentally):
if a.year > b.year:
return 1
if a.year < b.year:
return -1
if a.month > b.month:
return 1
if a.month < b.month:
return -1
# etcetera
return 0

This is decided NOT a language-neutral question. Java and Javasript and every SQL implementation I've ever used, for example, have date/time compare functions that work just fine. If the language or platform that you are using does not, you'd have to tell us what they are for anyone to be able to tell you what the correct function call to use is or how to work around some idiosyncracy. Yes, many languages have a date/time datatype, but that doesn't make the question language neutral. Most languages have a way to output text to the screen, but that doesn't make it legitimate to ask, "How do I write a text string to the screen in a language neutral way?" There is no answer to such a question.
I have no idea what you mean by "it only compares by last comparison". Again, you are apparently referring to the behavior of some particular comparison function in some particular language. You have to be more specific to get a meaningful answer.
Edit in reply to your comment: Different languages have different ways of storing dates. In Java, to see if one date/time is after another you write "date1.after(date2)" or "date1.compareTo(date2)>0". In Postgres SQL you write "date1>date2" or "date1::timestamp>date2::timestamp" depending on type definitions. In C there are a variety of "right" ways, including "difftime(date1, date2)". Etc etc.
Aaron's answer above is logically correct but irrelevant to how you actually compare dates in any language I use. If you had dates stored as a set of integers for each of these components (year, month, day, etc.), sure that would work. But I don't know of any language that stores dates that way.
If you have some reason for having your own format for storing dates, and breaking them out into components like this is useful for your application, then I suppose Aaron's technique is correct. But it is very unlikely that this is a useful format. Internally, Java stores date/time as number of milliseconds since midnight, Jan 1, 1970. C uses number of seconds. I'm sure other languages use other schemes.
You can, of course, write language-neutral pseudocode to compare two dates, just like you could write language-neutral pseudocode to describe writing to a screen. If your goal is to abstract the technical details of a language away while you work on a bigger problem, great. But if your goal is to actually compare two dates and find which is later or how many days are between them, then the answer lies in the technical details of how the particular language that you are using stores and manipulates dates.

I assume your problem is with localities. Assuming it's .NET, your dates contain localities, which are included in comparisons
dt1.CompareTo(dt2)
If you want to manually do it, then you should convert to ToUniversalTime() each and then compare.

Related

Querying https://musicbrainz.org for all artists

How can I query for all artists who were born after 1720 and died before 1900 on https://musicbrainz.org?
I need to retrieve their IDs and some information about them.
Is it possible to get data in JSON format?
for those who dont want to read a long post, here is everything the OP asked for in only one query:
http://musicbrainz.org/ws/2/artist/?query=begin:[1720 TO 1900] AND end:[1720 TO 1900] AND type:"person"&fmt=json
This should return perfect results, and has got to be the best answer possible.
- all artists, born after 1720 and dead before 1900, in json format, which retrieves their IDs, and lots of information about them...
The explanation and thought process:
Since Brian's currently accepted answer includes a link to the API document, i can say it is technically complete but I don't consider pointing to the spec a the best possible answer, and can be greatly improved.
Firstly it is easy to return json by adding the json format parameter.
&fmt=json
Secondly while i don't reckon there where many boy bands back in the day, given that OP is asking about births and deaths we may conclude they are interested in only people rather than groups other types of artists.
AND type:"person"
At which point as Brian suggests another call for each end date and then filter the results taking only those who died by 1900.
If you did this you would need to do way more than 180 searches the best answer suggests, but rather one for each birth and each death year combination, so technically 1720 to 1720, all the way through 1900-1900, my math stinks but that is thousands of searches.
But what makes this still such a horrible search is because sometimes dates are either written with only the year, and then sometimes written with month date and year, so for example if you search for begin 1929 and end 1900
So if a date is written to include not only year but month/date you would not get any results for this artist because of the full birthday:
ex:
id "2b8a16a9-468f-49b0-93ea-5e6726f41643" type "Person" life-span
begin "1929-11-10"
end "1990"
ended true
Therefore in order to get any good results using only the year you would need to add the fuzzy search syntax
musicbrainz.org/ws/2/artist/?query=begin:1960~ AND end:1990~ AND
type:"person"&fmt=json
But this does nothing to solve big problem of the magnitude of searches suggested, so knowing its LUCENS based I decided to learn some LUCENS, and realize there is range syntax:
Therefor you can do all of the above with one query:
http://musicbrainz.org/ws/2/artist/?query=begin:[1720 TO 1900] AND
end:[1720 TO 1900] AND type:"person"&fmt=json
PS I recommend to start adding quotes or even url encoding your parameter values to prevent breakage.
For example leaving quotes off begin and end numerals in the example above has no problem but off the type value will fail.
First, Musicbrainz only returns XML, as far as I know, so you'll have to convert the results to JSON.
To answer your question, it doesn't look like you'll be able to get the data you want in a single call. (The following is based off the XML Web Service Search documentation.)
This call will retrieve all artists who were born in a given year:
http://musicbrainz.org/ws/2/artist/?query=begin:1720
I believe you'd need to write 180 calls (one for each year between 1720 and 1900) to get the data you need. You'd also need to manually filter out artists who died after 1900, by looking at the <end> node within <life-span>. This is because the end field will only get you artists who died in a specific year.

How do I write a query that works in both HSQLDB and MySQL to add days to a date in the WHERE clause?

I'd like to add a specific number of days to a date column and then compare that to the current date. Is there any way I can achieve this that is compatible with both HSQLDB (for testing) and MySQL (production)?
To make things even more complicated, I am using Hibernate.
Don't know the background of your problem, but can you do it the other way around: compare dae column with current date substracted by number of days?
I meant something like this in hql (I'm using jpql in this example, if you prefer JPA interface):
TypedQuery<SomeClass> q = getEntityManager().crateQuery(
"select s from SomeClass s where s.date = :date", SomeClass.class);
Calendar now = Calendar.getInstance();
// subtract number of days from current date, e.g. 10 days
now.add(Calendar.DAY, -10);
q.setParameter("date", now);
List<SomeClass> = q.getResultList();
This approach is database-agnostic, but of course works only for trivial cases, in some mor e complicated cases it will not work.

Check to see if one year has passed

I run a web design firm, and we also sell hosting on a yearly basis to our customers. I'm writing a Ruby script to read all of the customers from a MySQL database and check to see if a year has passed. If it has (or is at least close to expiring), I want it to send an Email to them.
I have the Emailing part down, but how would I check to see if one year has passed using the Time class, or another solution based in SQL?
(looking back with this edit, it seems as though I don't quite know the SQL needed, so any help on that is appreciated)
If you really want to solve this issue using ruby you can use >> when dealing with a Date to increment it by N months (such as 11, which will give your customers some time to deal with the payment).
When you've got this incremented date you should compare it to what the current date is to see if it's less or equal (ie. the date is in the past), if so; send out your email.
>> (Date.strptime('2012-07-07') >> 11).to_s
=> "2013-06-07"
>> if (Date.strptime('2011-06-15') >> 11) < DateTime.now
>> print "More than 11 months has passed!"
>> end
More than 11 months has passed!=> nil
Though, you are much better of solving this issue directly in the SQL query, which would boil down to something as the below:
SELECT field1, field2, ..., fieldN
FROM `customers_table`
WHERE ADDDATE(last_payment, INTERVAL 11 MONTHS) <= NOW ()

ms access db order by month asc issue

In my database i have month name as january, february, march like that.
I represent field name is text. When i execute the select query with order by month field month, it will provide the output as february, january like that.
I know it is clear by the FORMAT or db field representation. Though i am new to the ms access so i don't know the correct format for this. Please help me.
You will have to create your own custom function in a module that cnverts the value for you.
Something like
Public Function StrToMonth(strIn As String) As Integer
Dim arrMonth(12) As Variant
Dim i As Integer
arrMonth(0) = "January"
arrMonth(1) = "February"
arrMonth(2) = "March"
arrMonth(3) = "April"
arrMonth(4) = "May"
arrMonth(5) = "June"
arrMonth(6) = "July"
arrMonth(7) = "August"
arrMonth(8) = "September"
arrMonth(9) = "October"
arrMonth(10) = "November"
arrMonth(11) = "December"
For i = 0 To UBound(arrMonth) - 1
If strIn = arrMonth(i) Then
StrToMonth = i + 1
Exit Function
End If
Next i
End Function
And then you can use it in your queries like
SELECT Table1.MonthVal
FROM Table1
ORDER BY StrToMonth([MonthVal]);
Add an additional column to the table(s) with month names that have the month-number-in-year, or much better, don't store months as strings, instead store them as dates e.g. 1 August 2010, which will make this problem considerably easier to solve.
Store the months as integers as currently your rows are returned using alpabetic sorting (how should database know that these strings there are months?): April, August, February, January, March...
You can convert the month to a date for the purpose of sorting:
SELECT MonthName
FROM SomeTable
ORDER BY CDate("1/" & [MonthName] & "/2010");
If you've decided it's inappropriate to store the months as actual dates (and it certainly could be -- I'm not criticizing that decision), you want to maximize performance by storing your data in a format that is most efficient.
That is most likely storing the month as an integer.
For display purposes, say in reports, or on a form, you can display the month name using format. It's a little tricky, as there's no direct conversion (where "MonthField" refers to the field where you're storing the month integer):
Format(DateSerial(Year(Date()), MonthField, 1), "mmmm")
An alternative would be to have a table that maps a month integer to the month name, but that adds a join, and if the field can be blank, you'd have to have an outer join, and that's much more expensive performance-wise than an inner join.
Keep in mind that you'd use the Format() statement only in the presentation layer, i.e., as the controlsource of a control on a form or report. On forms you'd likely use a 2-column combo box for this purpose, so the user would see the month name, but the value stored would actually be the month integer. In that case, a table might make sense, but I'm not certain that it would have any real advantage over just typing in a value list -- it's not like this is volatile data, i.e., data that may need to be edited (which is the main reason you'd use a table in place of a value list)!
EDIT:
As #HansUp has pointed out, there's a MonthName() function in VBA (I didn't know it existed!), so that makes all the above way more complicated than it needs to be -- you could use that in the presentation layer without a need for a table or for the complicated Format() statement.

How to store approximate dates in MySQL?

I need to store dates such as 'Summer 1878' or 'Early June 1923', or even 'Mid-afternoon on a Tuesday in August'. How would you suggest I do this?
I have considered breaking the date and time up into separate (integer) columns, and giving each column an ancillary (integer) column containing a range (0 if exact; NULL if unknown). But I'm sure there's other ways...
Thanks!
Since 'Mid-afternoon on a Tuesday in August' ("A Sunday Afternoon on the Island of La Grande Jatte"?) doesn't specify a year, the only real solution is your table of all date and time components, all nullable.
Other wise, you're conflating your data.
You have two (admittedly related) things here: a human readable string, the date_description, and a range of possible dates.
If you can specify at least a range, you can do this:
create table artwork {
artwork_id int not null primary key,
name varchar(80),
... other columns
date_description varchar(80),
earliest_possible_creation_date datetime
latest_possible_creation_date datetime
}
insert into artwork(
name,
date_description,
earliest_possible_creation_date,
latest_possible_creation_date
) values (
'A Sunday Afternoon on the Island of La Grande Jatte',
'Mid-afternoon on a Tuesday in August'
'1884-01-01',
'1886-12-31'
), (
'Blonde Woman with Bare Breasts',
'Summer 1878'
'1878-05-01',
'1878-08-31'
), (
'Paulo on a Donkey',
'Early June 1923',
'1923-06-01'
'1923-06-15'
);
This allows you to display whatever you want, and search for:
select * from artwork
where #some_date between
earliest_possible_creation_date and latest_possible_creation_date;
And obviously, "creation date" (the date the artist created the work) is entirely differnet from "date depicted in work", if the latter can be determined at all.
I'm using Postgres, and I wanted to do the same thing. Perhaps you can do it the same way as I did it, if MySQL has some similar geometric types: http://www.electricwords.org/2008/11/fuzzy-date-matching-in-postgresql/
Almost no matter what you do, you almost certainly won't be able to get the database to do the heavy lifting for you. So you are left with two options:
1 - Use natural strings as you have described
2 - Store a precise data as well as the precision of that date
For example, you could store "5:10:23pm on Sep 23,1975", "plus or minus 6 months", and when someone wants to search for records that occured in that timeframe this could pop up.
This doesn't help with queries, because to the best of my knowledge MySQL doesn't provide any support for tolerances ( nor do any others I know of ). You have to basically query it all and then filter out yourself.
I don't think any native MySQL date representation is going to work for you. Your two-column solution would work well if paired with a Unix time stamp (generated with the UNIX_TIMESTAMP() function with a MySQL date as the argument). Use the second column (the range width) for an upper and lower bound in your selects, and make sure the date column is indexed.
In the end I decided upon: a column for each of the date components (year, month, day, hour, minute, second), and accompanying columns for the range of each of these (year_range, month_range, day_range, hour_range, minute_range, second_range), mainly because this method allows me to specify that I know for sure that a particular photo was taken in August (for instance) in the late '60s (year=1868, year_range=2, month=8, month_range=0).
Thank you all for your help!
create a table with a list of values that you could want, like "Early" or "Summer". then whatever you have setting up the data could have an algorithm that sets a foreign key depending on the date.
Going with Chris Arguin's answer, in the second column just have another datetime column that you can use to store the +/-, then you should be able to write a query that uses both columns to get an approximate datetime.
Use two dates and determine the start and end date of the fuzzy region. For stuff like Summer 1878, enter 18780621 to 18780920. For Early June 1923 you have to decide when early ends, maybe 19230601 to 19230610. This makes it possible to select against the values. You might still want to filter afterward but this will get you close.
For the ones without years, you'll have to find a different system.