]> git.phdru.name Git - m_lib.git/blob - m_lib/opdate.py
Remove wrong copyright lines, fix module docstrings
[m_lib.git] / m_lib / opdate.py
1 #! /usr/bin/env python
2 # -*- coding: koi8-r -*-
3
4 #
5 # opDate - date/time manipulation routines
6 # Some ideas came from Turbo Professional/Object Professional (t/o)pDate.PAS
7 #
8
9
10 from string import *
11 from time import *
12 from calendar import *
13 from opstring import *
14
15
16 MinYear = 1600
17 MaxYear = 3999
18 MinDate = 0x00000000 # = 01/01/1600
19 MaxDate = 0x000D6025 # = 12/31/3999
20 Date1900 = 0x0001AC05 # = 01/01/1900
21 Date1980 = 0x00021E28 # = 01/01/1980
22 Date2000 = 0x00023AB1 # = 01/01/2000
23 BadDate = 0x7FFFFFFF
24
25 Threshold2000 = 1900
26
27 MinTime = 0          # = 00:00:00 am
28 MaxTime = 86399      # = 23:59:59 pm
29 BadTime = 0x7FFFFFFF
30
31 SecondsInDay = 86400 # number of seconds in a day
32 SecondsInHour = 3600 # number of seconds in an hour
33 SecondsInMinute = 60 # number of seconds in a minute
34 HoursInDay = 24      # number of hours in a day
35 MinutesInHour = 60   # number of minutes in an hour
36
37 First2Months = 59    # 1600 was a leap year
38 FirstDayOfWeek = 5   # 01/01/1600 was a Saturday
39
40
41 # Errors
42 class opdate_error(Exception):
43    pass
44
45
46 #
47 ### Date manipulation routines
48 #
49
50 def IsLeapYear(Year):
51    if ( (Year % 4 == 0) and (Year % 4000 <> 0) and ((Year % 100 <> 0) or (Year % 400 == 0)) ):
52       return True
53    return False
54
55
56 def _setYear(Year):
57    # Internal function
58    if Year < 100:
59       Year = Year + 1900
60       if Year < Threshold2000:
61          Year = Year + 100
62    return Year
63
64
65 def DaysInMonth(Month, Year):
66    """ Return the number of days in the specified month of a given year """
67    if Month in [1, 3, 5, 7, 8, 10, 12]:
68       return 31
69
70    elif Month in [4, 6, 9, 11]:
71       return 30
72
73    elif Month == 2:
74       return 28+IsLeapYear(_setYear(Year))
75
76    else:
77       raise opdate_error, "bad month `%s'" % str(Month)
78
79
80 def ValidDate(Day, Month, Year):
81    """ Verify that day, month, year is a valid date """
82    Year = _setYear(Year)
83
84    if (Day < 1) or (Year < MinYear) or (Year > MaxYear):
85       return False
86    elif (Month >= 1) and (Month <= 12):
87          return Day <= DaysInMonth(Month, Year)
88    else:
89       return False
90
91
92 def DMYtoDate(Day, Month, Year):
93    """ Convert from day, month, year to a julian date """
94    Year = _setYear(Year)
95
96    if not ValidDate(Day, Month, Year):
97       return BadDate
98
99    if (Year == MinYear) and (Month < 3):
100       if Month == 1:
101          return Day-1
102       else:
103          return Day+30
104    else:
105       if Month > 2:
106          Month = Month - 3
107       else:
108          Month = Month + 9
109          Year = Year - 1
110       Year = Year - MinYear
111
112       return (((Year / 100)*146097) / 4) + (((Year % 100)*1461) / 4) + (((153*Month)+2) / 5)+Day+First2Months
113
114
115 def DateToDMY(Julian):
116    """ Convert from a julian date to day, month, year """
117    if Julian == BadDate:
118       return 0, 0, 0
119
120    if Julian <= First2Months:
121       Year = MinYear
122       if Julian <= 30:
123          Month = 1
124          Day = Julian + 1
125       else:
126          Month = 2
127          Day = Julian-30
128    else:
129       I = (4*(Julian-First2Months))-1
130       J = (4*((I % 146097) / 4))+3
131       Year = (100*(I / 146097))+(J / 1461)
132       I = (5*(((J % 1461)+4) / 4))-3
133       Month = I / 153
134       Day = ((I % 153)+5) / 5
135       if Month < 10:
136          Month = Month + 3
137       else:
138          Month = Month - 9
139          Year = Year + 1
140       Year = Year + MinYear
141
142    return Day, Month, Year
143
144
145 def IncDate(Julian, Days, Months, Years):
146    """ Add (or subtract) the number of months, days, and years to a date.
147        Months and years are added before days. No overflow/underflow checks are made
148    """
149    Day, Month, Year = DateToDMY(Julian)
150    Day28Delta = Day-28
151    if Day28Delta < 0:
152       Day28Delta = 0
153    else:
154       Day = 28
155
156    Year = Year + Years
157    Year = Year + Months / 12
158    Month = Month + Months % 12
159    if Month < 1:
160       Month = Month + 12
161       Year = Year - 1
162    elif Month > 12:
163       Month = Month - 12
164       Year = Year + 1
165
166    Julian = DMYtoDate(Day, Month, Year)
167    if Julian <> BadDate:
168       Julian = Julian + Days + Day28Delta
169
170    return Julian
171
172
173 def IncDateTrunc(Julian, Months, Years):
174    """ Add (or subtract) the specified number of months and years to a date """
175    Day, Month, Year = DateToDMY(Julian)
176    Day28Delta = Day-28
177    if Day28Delta < 0:
178       Day28Delta = 0
179    else:
180       Day = 28
181
182    Year = Year + Years
183    Year = Year + Months / 12
184    Month = Month + Months % 12
185    if Month < 1:
186       Month = Month + 12
187       Year = Year - 1
188    elif Month > 12:
189       Month = Month - 12
190       Year = Year + 1
191
192    Julian = DMYtoDate(Day, Month, Year)
193    if Julian <> BadDate:
194       MaxDay = DaysInMonth(Month, Year)
195       if Day+Day28Delta > MaxDay:
196          Julian = Julian + MaxDay-Day
197       else:
198          Julian = Julian + Day28Delta
199
200    return Julian
201
202
203 def DateDiff(Date1, Date2):
204    """ Return the difference in days,months,years between two valid julian dates """
205    #we want Date2 > Date1
206    if Date1 > Date2:
207       _tmp = Date1
208       Date1 = Date2
209       Date2 = _tmp
210
211    #convert dates to day,month,year
212    Day1, Month1, Year1 = DateToDMY(Date1)
213    Day2, Month2, Year2 = DateToDMY(Date2)
214
215    #days first
216    if Day2 < Day1:
217       Month2 = Month2 - 1
218       if Month2 == 0:
219          Month2 = 12
220          Year2 = Year2 - 1
221       Day2 = Day2 + DaysInMonth(Month2, Year2)
222    Days = abs(Day2-Day1)
223
224    #now months and years
225    if Month2 < Month1:
226       Month2 = Month2 + 12
227       Year2 = Year2 - 1
228    Months = Month2-Month1
229    Years = Year2-Year1
230
231    return Days, Months, Years
232
233
234 def DayOfWeek(Julian):
235    """ Return the day of the week for the date. Returns DayType(7) if Julian == BadDate. """
236    if Julian == BadDate:
237       raise opdate_error, "bad date `%s'" % str(Julian)
238    else:
239       return (Julian+FirstDayOfWeek) % 7
240
241
242 def DayOfWeekDMY(Day, Month, Year):
243    """ Return the day of the week for the day, month, year """
244    return DayOfWeek( DMYtoDate(Day, Month, Year) )
245
246
247 #def MonthStringToMonth(MSt):
248 #   """ Convert the month name in MSt to a month (1..12) or -1 on error """
249 #   lmn = strptime.LongMonthNames[strptime.LANGUAGE]
250 #   smn = strptime.ShortMonthNames[strptime.LANGUAGE]
251 #   lmna = LongMonthNamesA
252 #
253 #   I = FindStr(MSt, lmn)+1 or FindStr(MSt, smn)+1 or \
254 #      FindStrUC(MSt, lmn)+1 or FindStrUC(MSt, smn)+1 or \
255 #      FindStr(MSt, lmna)+1 or FindStrUC(MSt, lmna)+1
256 #
257 #   return I-1
258
259
260 def Today():
261    """ Returns today's date as a julian """
262    Year, Month, Day = localtime(time())[0:3]
263    return DMYtoDate(Day, Month, Year)
264
265 #
266 ### Time manipulation routines
267 #
268
269 def TimeToHMS(T):
270    """ Convert a Time variable to Hours, Minutes, Seconds """
271    if T == BadTime:
272       return 0, 0, 0
273
274    else:
275       Hours = T / SecondsInHour
276       T = T - Hours*SecondsInHour
277       Minutes = T / SecondsInMinute
278       T = T - Minutes*SecondsInMinute
279       Seconds = T
280
281       return Hours, Minutes, Seconds
282
283
284 def HMStoTime(Hours, Minutes, Seconds):
285    """ Convert Hours, Minutes, Seconds to a Time variable """
286    Hours = Hours % HoursInDay
287    T = Hours*SecondsInHour + Minutes*SecondsInMinute + Seconds
288
289    return T % SecondsInDay
290
291
292 def ValidTime(Hours, Minutes, Seconds):
293    """ Return true if Hours:Minutes:Seconds is a valid time """
294    return (0 <= Hours < 24) and (0 <= Minutes < 60) and (0 <= Seconds < 60)
295
296
297 def CurrentTime():
298    """ Returns current time in seconds since midnight """
299    Hours, Minutes, Seconds = localtime(time())[3:6]
300    return HMStoTime(Hours, Minutes, Seconds)
301
302
303 def TimeDiff(Time1, Time2):
304    """ Return the difference in hours,minutes,seconds between two times """
305    if Time1 > Time2:
306       T = Time1-Time2
307    else:
308       T = Time2-Time1
309
310    Hours, Minutes, Seconds = TimeToHMS(T)
311    return Hours, Minutes, Seconds
312
313
314 def IncTime(T, Hours, Minutes, Seconds):
315    """ Add the specified hours,minutes,seconds to T and return the result """
316    T = T + HMStoTime(Hours, Minutes, Seconds)
317    return T % SecondsInDay
318
319
320 def DecTime(T, Hours, Minutes, Seconds):
321    """ Subtract the specified hours,minutes,seconds from T and return the result """
322    Hours = Hours % HoursInDay
323    T = T - HMStoTime(Hours, Minutes, Seconds)
324    if T < 0:
325       return T+SecondsInDay
326    else:
327       return T
328
329
330 def RoundToNearestHour(T, Truncate = False):
331    """ Round T to the nearest hour, or Truncate minutes and seconds from T """
332    Hours, Minutes, Seconds = TimeToHMS(T)
333    Seconds = 0
334
335    if not Truncate:
336       if Minutes >= (MinutesInHour / 2):
337          Hours = Hours + 1
338
339    Minutes = 0
340    return HMStoTime(Hours, Minutes, Seconds)
341
342
343 def RoundToNearestMinute(T, Truncate = False):
344    """ Round T to the nearest minute, or Truncate seconds from T """
345    Hours, Minutes, Seconds = TimeToHMS(T)
346
347    if not Truncate:
348       if Seconds >= (SecondsInMinute / 2):
349          Minutes = Minutes + 1
350
351    Seconds = 0
352    return HMStoTime(Hours, Minutes, Seconds)
353
354
355 def DateTimeDiff(DT1, DT2):
356    """ Return the difference in days,seconds between two points in time """
357    # swap if DT1 later than DT2
358    if (DT1[0] > DT2[0]) or ((DT1[0] == DT2[0]) and (DT1[1] > DT2[1])):
359       _tmp = DT1
360       DT1 = DT2
361       DT2 = _tmp
362
363    # the difference in days is easy
364    Days = DT2[0]-DT1[0]
365
366    # difference in seconds
367    if DT2[1] < DT1[1]:
368       # subtract one day, add 24 hours
369       Days = Days - 1
370       DT2[1] = DT2[1] + SecondsInDay
371
372    Secs = DT2[1]-DT1[1]
373    return Days, Secs
374
375
376 def IncDateTime(DT1, Days, Secs):
377    """ Increment (or decrement) DT1 by the specified number of days and seconds
378       and put the result in DT2 """
379    DT2 = DT1[:]
380
381    # date first
382    DT2[0] = DT2[0] + Days
383
384    if Secs < 0:
385       # change the sign
386       Secs = -Secs
387
388       # adjust the date
389       DT2[0] = DT2[0] - Secs / SecondsInDay
390       Secs = Secs % SecondsInDay
391
392       if Secs > DT2[1]:
393          # subtract a day from DT2[0] and add a day's worth of seconds to DT2[1]
394          DT2[0] = DT2[0] - 1
395          DT2[1] = DT2[1] + SecondsInDay
396
397       # now subtract the seconds
398       DT2[1] = DT2[1] - Secs
399
400    else:
401       # increment the seconds
402       DT2[1] = DT2[1] + Secs
403
404       # adjust date if necessary
405       DT2[0] = DT2[0] + DT2[1] / SecondsInDay
406
407       # force time to 0..SecondsInDay-1 range
408       DT2[1] = DT2[1] % SecondsInDay
409
410    return DT2
411
412
413 #
414 ### UTC (GMT) stuff
415 #
416
417 UTC_0Date = DMYtoDate(1, 1, 1970)
418
419
420 def DateTimeToGMT(Date, Time = False):
421    Date = Date - UTC_0Date
422    return Date*SecondsInDay + Time
423
424
425 def GMTtoDateTime(GMT):
426    q, r = divmod(GMT, SecondsInDay)
427    return q + UTC_0Date, r
428
429
430 #
431 ### Cyrillic stuff
432 #
433
434 LongMonthNamesA = ['Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня',
435    'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря']
436
437
438 #
439 ### Test stuff
440 #
441
442 def test():
443    print "Is 1984 leap year?", IsLeapYear(1984)
444    print "Is 1990 leap year?", IsLeapYear(1990)
445
446    print "Days in month 8 year 1996:", DaysInMonth(8, 1996)
447
448    print "Is date 8/12/1996 valid?", ValidDate(8, 12, 1996)
449    print "Is date 40/11/1996 valid?", ValidDate(40, 11, 1996)
450    print "Is date 8/14/1996 valid?", ValidDate(8, 14, 1996)
451
452    print "Date->DMY for 138219:", DateToDMY(138219)
453
454    diff = DateDiff(DMYtoDate(12, 10, 1996), DMYtoDate(12, 10, 1997))
455    print "Date 12/10/1996 and date 12/10/1997 diff: %d years, %d months, %d days" % (diff[2], diff[1], diff[0])
456
457    diff = DateDiff(DMYtoDate(12, 10, 1996), DMYtoDate(12, 11, 1997))
458    print "Date 12/10/1996 and date 12/11/1997 diff: %d years, %d months, %d days" % (diff[2], diff[1], diff[0])
459
460    diff = DateDiff(DMYtoDate(31, 1, 1996), DMYtoDate(1, 3, 1996))
461    print "Date 31/01/1996 and date 01/03/1996 diff: %d years, %d months, %d days" % (diff[2], diff[1], diff[0])
462
463
464    #print "November is %dth month" % MonthStringToMonth("November")
465
466    print "Today is", Today()
467    print "Now is", CurrentTime()
468
469    print "My birthday 21 Dec 1967 is (must be Thursday):", day_name[DayOfWeekDMY(21, 12, 67)]
470
471    gmt = DateTimeToGMT(DMYtoDate(21, 12, 1967), HMStoTime(23, 45, 0))
472    print "21 Dec 1967, 23:45:00 --", gmtime(gmt) # DOS version of gmtime has error processing dates before 1/1/1970 :(
473    D, T = GMTtoDateTime(gmt)
474    print "(gmt) --", DateToDMY(D), TimeToHMS(T)
475
476 if __name__ == "__main__":
477    test()