Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Something for the weekend #5: Testing for a leap year 6

Status
Not open for further replies.

Mike Lewis

Programmer
Jan 10, 2003
17,505
Scotland
After our excursion into American TV history, today's exercise returns to VFP programming fundamentals.

Your task is to write a function that receives a year (e.g. 2015), and returns .T. if the year is a leap year.

That's all there is to it. As usual, kudos will go to the person who suggests the smallest function (that is, the least amount of code). It doesn't have to be particularly readable or easy to understand.

The function must not have any side effects. If it changes any aspect of the environment (such as SET values), it must change it back again.

If you are one of the first to respond, please use
tags to hide your answer. (An easy way to insert these tags is to use the little yellow icon to the right of the
Code:
 icon at the top of the editing window.)

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

[url=http://www.ml-consult.co.uk/foxstuff.htm]Visual FoxPro articles, tips and downloads[/url]
 
Regarding my follow-up question:

Tore Bleken said:
Because September 14, 1752 is the first legal date in VFP.

No, it's not. The first legal date is 1 Janaury 1 (that's the year 1, the first year CE).

[tt]lDate = {^0001-01-01}
? lDate, DAY(lDate), lDate + 1[/tt]

These all give valid results.

September 14, 1752 is the first valid date for SYS(1) and GOMONTH(). But my question is: why?

Tamar said:
On the 1752 question, the key issue is that 1752 is when the British empire adopted the Gregorian calendar.

That's right. The calendar was advanced eleven days, leading some people to believe that their lives had somehow been shortened by that amount. But it still doesn't really explain why GOMONTH() doesn't work with earlier dates, but most other VFP date-related functions do.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Yes, as always it was a nice exercise.

In regard to code abbreviations I agree fully with you, especially since the compiled result is similar anyway:

Code:
Local lcPRG

Text to lcPRG NOSHOW
FUNC IsLeapYear(nYear)
RETU ! EMPT(DATE(m.nYear, 2, 29))
EndText
StrToFile(lcPrg,"D:\temp\f1.prg")
Compile D:\temp\f1.prg

Text to lcPRG NOSHOW
FUNCTION IsLeapYear(nYear)
Return NOT EMPTY(DATE(m.nYear, 2, 29))
EndText
StrToFile(lcPrg,"D:\temp\f2.prg")
Compile D:\temp\f2.prg

Compare f1.fxp and f2.fxp in Hexedit and you'll see they don't differ in the code section. There is a difference is the file name included at the end of the fxp file.
Both files are 244 bytes long, so there is no reason for abbreviating source code.

Bye, Olaf.
 
I found the behavior of GOMONTH() very interesting.
That made me curious and I played with other date functions.
Code:
SET CENTURY ON
?DATE(100,1,1) && correct
?DATE(99,12,31) && raise an error
It appears that the first valid date is the First of January form the year 100.

But subtracting one day :
Code:
SET CENTURY ON
?DATE(100,1,1)-1 && correct date
?DATE(100,1,1)-1<DATE(100,1,1) && .T.
?YEAR(DATE(100,1,1)-1) && 99
So I subtract 100,1000,10000 days until :
Code:
?DATE(100,1,1)-36465
First of March of the year "0". Well, year "0" doesn't exists in the common sense (1 BC is followed by 1 AD), although I found a possible explanation
If I subtract one more day
Code:
?DATE(100,1,1)-36466
?CDOW(DATE(100,1,1)-36466)
This is really an oddity, because is the first time I heard of "Zeroth of March"

And before that the result is the empty date.
Code:
?DATE(100,1,1)-36467
?CDOW(DATE(100,1,1)-36467)
Now, could be DATE(100,1,1)-36466 the day 0 ?
Somewhere I read that before First of January as the beginning of year, the Romans used First of March as the New Year Day (something about the Roman Senators first entry or something like that)



Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
>Somewhere I read that before First of January as the beginning of year, the Romans used First of March as the New Year Day.

Yes, several month names also indicate the month numbers have been altered, september, october, november, december must have been the 7th to 10th month before calender reformation. It's more natural to start the year with the spring season, isn't it?

Bye, Olaf.
 
Vilhelm-Ion said:
?DATE(99,12,31) && raise an error

It appears that the first valid date is the First of January form the year 100.

I don't think so. I think you get the error because of a limitation of the DATE() function, not because 1 Jan 100 is the first valid date:

VFP Help for DATE() function said:
[tt]DATE([nYear, nMonth, nDay])[/tt]
nYear can be a value from 100 to 9999.

The following does not give an error, indicating the 1 Jan 1 is a valid date:

[tt]? {^0001-01-01}[/tt]

What about year 0? I assume that, becaue that year never existed (did it?), dates earlier than 1 Jan 1 will be invalid. But don't be fooled by this:

[tt]SET STRICTDATE TO 0
SET DATE AMERICAN
? {1-1-0}[/tt]

It looks like the year there is 0, but of course it's not. (I'm sure you can all see why.)

Enough from me for now.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
?{^0000-12-31} generate an error because of a limitation of the date constant format, because there are "valid" dates back to Zeroth of March, Year Zero ;-) (in help is specified {^0001-01-01} ... to {^9999-12-31})

Code:
?{^0001-1-1},YEAR({^0001-1-1}),{^0001-1-1}-1,YEAR({^0001-1-1}-1),{^0001-1-1}-307,YEAR({^0001-1-1}-307),{^0001-1-1}-308,CDOW({^0001-1-1}-308)
?MONTH({^0001-1-1}-307),MONTH({^0001-1-1}-308),DAY({^0001-1-1}-307),DAY({^0001-1-1}-308),CDOW({^0001-1-1}-307)
dd={^0001-1-1}-307
?dd,YEAR(dd),MONTH(dd),DAY(dd),CDOW(dd)
?dd+365*2000,GOMONTH(dd+365*2000,1)


Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
If you want to overcome the date value range of VFP you may compute juliandaynumber and work from there. SYS(11,dDate) helps, but of course has the same limitations as the date type, as it expects a date as parameter (or a datetime, but it's the same result for any time of a certain date).

The integer type covers lots more dates, if you want and the formula is quite simple:
Code:
Function JulianDayNumber(tnYear, tnMonth, tnDay)
   Local lnYears, lnMonths, lnDays
   Local lnJulianDayNumber

   lnYears  = tnYear
   lnMonths = tnMonth-3
   lnDays = tnDay-1
   If lnMonths<0
      lnYears = lnYears - 1
      lnMonths = lnMonths + 12
   Endif
   
   lnJulianDayNumber =        ;
      1721120                 ;
      +lnYears*365            ;
      +Int(lnYears/4)         ;
      -Int(lnYears/100)       ;
      +Int(lnYears/400)       ;
      +Round(lnMonths*30.6,0) ;
      +lnDays

   Return lnJulianDayNumber
EndFunc

Bye, Olaf.
 
Actually it's not really correct, as historically the 15th of october 1582 directly followed the 4th of october 1582. 11 days were skipped when the gregorian calendar replaced the julian calendar.

Anyway, you might skip the offset 1721120 going back to 24th of November 4714 BC and will have the 01.03.01 AD As epoch for that day number. Once you have a day number and an epoch reference date you may compute further dates on that basis. It's of course very simple to compute date differences in days and add a number of days to a date, if any date is represented by a number. The same computation rules apply, no matter where you put the reference date.

Bye, Olaf.
 
This discussion was only for fun, at least for myself. Currently I don't use dates before 1900. The discussion was provoked by the GOMONTH() bug, discovered long ago by Mr. Mike Lewis.

First I must correct one of my previous assumptions.
The shift of the Romans New Year Day from 1 March to 1 January was around the year 46 BC.
I believe "year zero" is simply a bug, and 0 March, Year 0 is caused by the incapacity to determine what must be : 28 or 29 February.
But these are only speculations.

The Gregorian calender stated the length of an year to 365.2425 days and it was a better approximation than the Julian calender, whose length of the year is 365.25 days. The improvement was made by declaring 1700,1800,1900, 2100, 2200 and so on, as non-leap year.
But 1000 was a leap year. Also 1100 and 1300.
But not for VFP :
Code:
?DATE(1000,2,29) && should be valid
?{^1000-1-1} + 365.2425 * 100 && should give {^1099-12-31}
?{^1000-1-1} + 365.25 * 100 && should give {^1100-1-1}

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
>But 1000 was a leap year. Also 1100 and 1300.
I don't know about that. Were they defined so as exceptions of the rule?

The 365.2425 instead of 365.25 make a difference each 100 years and each 400 years, so the years dividable by 100 would not be leap years but 400, 800, 1200, 1600, and 2000 would still be leap years.
Since the gregorian calender is introduced 1582 only 1700, 1800 and 1900 were exceptions of the normal dividable by 4 rule and were no leap years, while 1600 and 2000 were leap years. And the differences so far before 1582 were accounted by the 11 day shift.

Besides, further even more precise corrections are made via leap seconds. It's inserted as an addition second at 23:59:60 of certain days. So you could say the exact time of midnight lasts one second, then, before clocks continue.

Bye, Olaf.

 
Now that I explained it myself, I see what you mean. Every century year before 1600 should have been a leap year as the year was defined as 365.25 days by the julien calender back then and so the simpler rule of a leap year every 4th year applies to 1500, 1400, 1300, 1200, 1100, 1000, and so on, too. VFP applies the gregorian calender rule to these centuries, which is also wrong. It is an error before 1752, when also british empire changed to gregorian. So the definition of VFP dates only being correct after that year still applies anyway.

Bye, Olaf.
 
So, just to wrap this up. Although my original question was trivially easy, this has been a very interesting discussion. I've learned some stuff, and I hope others have too. Thanks to all who contributed - and stars all round.

If anyone can come up with a suitable exercise for a future thread of this type, feel free.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike Lewis said:
I've learned some stuff, and I hope others have too
I certainly, do.
I wasn't aware, until now, of the GOMONTH() bug and I liked the alternative solutions.
I always like to see different solutions for the same problem, because each one is valuable, either in that or in another context.
You are the one who deserves five stars for the initiative and for the entertainment:)

Respectfully,
Vilhelm-Ion Praisach
Resita, Romania
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top