
# To run these tests, use:
#   python unittest.py DateTime.tests.suite

import math
import os
import time
import unittest

from DateTime.DateTime import _findLocalTimeZoneName
from DateTime import DateTime

try:
    __file__
except NameError:
    import sys
    f = sys.argv[0]
else:
    f = __file__

DATADIR = os.path.dirname(os.path.abspath(f))
del f


class DateTimeTests(unittest.TestCase):

    def _compare(self, dt1, dt2, ms=1):
        '''Compares the internal representation of dt1 with
        the representation in dt2.  Allows sub-millisecond variations.
        Primarily for testing.'''
        if ms:
            self.assertEqual(dt1.millis(), dt2.millis())
        self.assertEqual(math.floor(dt1._t * 1000.0),
                         math.floor(dt2._t * 1000.0))
        self.assertEqual(math.floor(dt1._d * 86400000.0),
                         math.floor(dt2._d * 86400000.0))
        self.assertEqual(math.floor(dt1.time * 86400000.0),
                         math.floor(dt2.time * 86400000.0))

    def testBug1203(self):
        '''01:59:60 occurred in old DateTime'''
        dt = DateTime(7200, 'GMT')
        self.assert_(str(dt).find('60') < 0, dt)

    def testDSTInEffect(self):
        '''Checks GMT offset for a DST date in the US/Eastern time zone'''
        dt = DateTime(2000, 5, 9, 15, 0, 0, 'US/Eastern')
        self.assertEqual(dt.toZone('GMT').hour(), 19,
                         (dt, dt.toZone('GMT')))

    def testDSTNotInEffect(self):
        '''Checks GMT offset for a non-DST date in the US/Eastern time zone'''
        dt = DateTime(2000, 11, 9, 15, 0, 0, 'US/Eastern')
        self.assertEqual(dt.toZone('GMT').hour(), 20,
                         (dt, dt.toZone('GMT')))

    def testAddPrecision(self):
        '''Precision of serial additions'''
        dt = DateTime()
        self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt),
                         dt)

    def testConstructor3(self):
        '''Constructor from date/time string'''
        dt = DateTime()
        dt1s = '%d/%d/%d %d:%d:%f %s' % (
            dt.year(),
            dt.month(),
            dt.day(),
            dt.hour(),
            dt.minute(),
            dt.second(),
            dt.timezone())
        dt1 = DateTime(dt1s)
        # Compare representations as it's the
        # only way to compare the dates to the same accuracy
        self.assertEqual(repr(dt),repr(dt1))

    def testConstructor4(self):
        '''Constructor from time float'''
        dt = DateTime()
        dt1 = DateTime(float(dt))
        self._compare(dt,dt1)

    def testConstructor5(self):
        '''Constructor from time float and timezone'''
        dt = DateTime()
        dt1 = DateTime(float(dt), dt.timezone())
        self.assertEqual(str(dt), str(dt1), (dt, dt1))

    def testConstructor6(self):
        '''Constructor from year and julian date'''
        # This test must normalize the time zone, or it *will* break when
        # DST changes!
        dt1 = DateTime(2000, 5.500000578705)
        dt = DateTime('2000/1/5 12:00:00.050 pm %s' % dt1.localZone())
        self._compare(dt, dt1)

    def testConstructor7(self):
        '''Constructor from parts'''
        dt = DateTime()
        dt1 = DateTime(
            dt.year(),
            dt.month(),
            dt.day(),
            dt.hour(),
            dt.minute(),
            dt.second(),
            dt.timezone())
        # Compare representations as it's the
        # only way to compare the dates to the same accuracy
        self.assertEqual(repr(dt), repr(dt1))

    def testDayOfWeek(self):
        '''strftime() used to always be passed a day of week of 0.'''
        dt = DateTime('2000/6/16')
        s = dt.strftime('%A')
        self.assertEqual(s, 'Friday', (dt, s))

    def testOldDate(self):
        '''Fails when an 1800 date is displayed with negative signs'''
        dt = DateTime('1830/5/6 12:31:46.213 pm')
        dt1 = dt.toZone('GMT+6')
        self.assert_(str(dt1).find('-') < 0, (dt, dt1))

    def testSubtraction(self):
        '''Reconstruction of a DateTime from its parts, with subtraction'''
        dt = DateTime()
        dt1 = dt - 3.141592653
        dt2 = DateTime(
            dt.year(),
            dt.month(),
            dt.day(),
            dt.hour(),
            dt.minute(),
            dt.second())
        dt3 = dt2 - 3.141592653
        self.assertEqual(dt1, dt3, (dt, dt1, dt2, dt3))

    def testTZ1add(self):
        '''Time zone manipulation: add to a date'''
        dt = DateTime('1997/3/8 1:45am GMT-4')
        dt1 = DateTime('1997/3/9 1:45pm GMT+8')
        self.assertEqual(dt + 1.0, dt1, (dt, dt1))

    def testTZ1sub(self):
        '''Time zone manipulation: subtract from a date'''
        dt = DateTime('1997/3/8 1:45am GMT-4')
        dt1 = DateTime('1997/3/9 1:45pm GMT+8')
        self.assertEqual(dt1 - 1.0, dt, (dt, dt1))

    def testTZ1diff(self):
        '''Time zone manipulation: diff two dates'''
        dt = DateTime('1997/3/8 1:45am GMT-4')
        dt1 = DateTime('1997/3/9 1:45pm GMT+8')
        self.assertEqual(dt1 - dt, 1.0, (dt, dt1))

    def testCompareMethods(self):
        '''Compare two dates using several methods'''
        dt = DateTime('1997/1/1')
        dt1 = DateTime('1997/2/2')
        self.failUnless(dt1.greaterThan(dt))
        self.failUnless(dt1.greaterThanEqualTo(dt))
        self.failUnless(dt.lessThan(dt1))
        self.failUnless(dt.lessThanEqualTo(dt1))
        self.failUnless(dt.notEqualTo(dt1))
        self.failUnless(not dt.equalTo(dt1))

    def testCompareOperations(self, dt=None, dt1=None):
        """Compare two dates using several operations"""
        if dt is None:
            dt = DateTime('1997/1/1')
        if dt1 is None:
            dt1 = DateTime('1997/2/2')
        self.failUnless(dt1 > dt)
        self.failUnless(dt1 >= dt)
        self.failUnless(dt < dt1)
        self.failUnless(dt <= dt1)
        self.failUnless(dt != dt1)
        self.failUnless(not (dt == dt1))

    def testUpgradeOldInstances(self):
        """Compare dates that don't have the _millis attribute yet."""
        dt = DateTime('1997/1/1')
        dt1 = DateTime('1997/2/2')
        del dt._millis
        del dt1._millis
        self.testCompareOperations(dt, dt1)

    def testTZ2(self):
        '''Time zone manipulation test 2'''
        dt = DateTime()
        dt1 = dt.toZone('GMT')
        s = dt.second()
        s1 = dt1.second()
        self.assertEqual(s, s1, (dt, dt1, s, s1))

    def testTZDiffDaylight(self):
        '''Diff dates across daylight savings dates'''
        dt = DateTime('2000/6/8 1:45am US/Eastern')
        dt1 = DateTime('2000/12/8 12:45am US/Eastern')
        self.assertEqual(dt1 - dt, 183, (dt, dt1, dt1 - dt))

    def testY10KDate(self):
        '''Comparison of a Y10K date and a Y2K date'''
        dt = DateTime('10213/09/21')
        dt1 = DateTime(2000, 1, 1)

        dsec = (dt.millis() - dt1.millis()) / 1000.0
        ddays = math.floor((dsec / 86400.0) + 0.5)

        self.assertEqual(ddays, 3000000L, ddays)

    def test_tzoffset(self):
        '''Test time-zone given as an offset'''

        # GMT
        dt = DateTime('Tue, 10 Sep 2001 09:41:03 GMT')
        self.assertEqual(dt.tzoffset(), 0)

        # Timezone by name, a timezone that hasn't got daylightsaving.
        dt = DateTime('Tue, 2 Mar 2001 09:41:03 GMT+3')
        self.assertEqual(dt.tzoffset(), 10800)

        # Timezone by name, has daylightsaving but is not in effect.
        dt = DateTime('Tue, 21 Jan 2001 09:41:03 PST')
        self.assertEqual(dt.tzoffset(), -28800)

        # Timezone by name, with daylightsaving in effect
        dt = DateTime('Tue, 24 Aug 2001 09:41:03 PST')
        self.assertEqual(dt.tzoffset(), -25200)

        # A negative numerical timezone
        dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0400')
        self.assertEqual(dt.tzoffset(), -14400)

        # A positive numerical timzone
        dt = DateTime('Tue, 6 Dec 1966 01:41:03 +0200')
        self.assertEqual(dt.tzoffset(), 7200)

        # A negative numerical timezone with minutes.
        dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0637')
        self.assertEqual(dt.tzoffset(), -23820)

        # A positive numerical timezone with minutes.
        dt = DateTime('Tue, 24 Jul 2001 09:41:03 +0425')
        self.assertEqual(dt.tzoffset(), 15900)

    def testISO8601(self):
        ''' iso 8601 dates '''

        ref0 = DateTime('2002/5/2 8:00am GMT')
        ref1 = DateTime('2002/5/2 8:00am US/Eastern')

        isoDt = DateTime('2002-05-02T08:00:00')
        self.assertEqual( ref0, isoDt)
        isoDt = DateTime('2002-05-02T08:00:00Z')
        self.assertEqual( ref0, isoDt)

        isoDt = DateTime('2002-05-02T08:00:00-04:00')
        self.assertEqual( ref1, isoDt)

        # Bug 1386: the colon in the timezone offset is optional
        isoDt = DateTime('2002-05-02T08:00:00-0400')
        self.assertEqual( ref1, isoDt)
        
        dgood = '2002-05-02'
        tgood = 'T08:00:00-04:00'
        for dbad in '2002-5-2', '2002-10-2', '2002-2-10', '02-2-10':
            self.assertRaises(DateTime.SyntaxError, DateTime, dbad)
            self.assertRaises(DateTime.SyntaxError, DateTime, dbad + tgood)
        for tbad in '08:00', 'T8:00': #, 'T08:00Z-04:00':
            self.assertRaises(DateTime.SyntaxError, DateTime, dgood + tbad)

        iso8601_string = '2002-05-02T08:00:00-04:00'
        iso8601DT = DateTime(iso8601_string)
        self.assertEqual(iso8601_string, iso8601DT.ISO8601())

    def testJulianWeek(self):
        """ check JulianDayWeek function """

        try:
            import gzip
        except ImportError:
            print "Warning: testJulianWeek disabled: module gzip not found"
            return 0

        fn = os.path.join(DATADIR, 'julian_testdata.txt.gz')
        lines = gzip.GzipFile(fn).readlines()

        for line in lines:
            d = DateTime(line[:10])
            result_from_mx=tuple(map(int, line[12:-2].split(',')))
            self.assertEqual(result_from_mx[1], d.week())

    def testCopyConstructor(self):
        d = DateTime('2004/04/04')
        self.assertEqual(DateTime(d), d)
        d = DateTime('1999/04/12')
        self.assertEqual(DateTime(d), d)

    def testRFC822(self):
        '''rfc822 conversion'''
        dt = DateTime('2002-05-02T08:00:00+00:00')
        self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0000')

        dt = DateTime('2002-05-02T08:00:00+02:00')
        self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0200')

        dt = DateTime('2002-05-02T08:00:00-02:00')
        self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 -0200')

        # Checking that conversion from local time is working.
        dt = DateTime()
        dts = dt.rfc822().split(' ')
        times = dts[4].split(':')
        _isDST = time.localtime(time.time())[8]
        if _isDST: offset = time.altzone
        else:      offset = time.timezone

        self.assertEqual(dts[0], dt.aDay() + ',')
        self.assertEqual(int(dts[1]), dt.day())
        self.assertEqual(dts[2], dt.aMonth())
        self.assertEqual(int(dts[3]), dt.year())
        self.assertEqual(int(times[0]), dt.h_24())
        self.assertEqual(int(times[1]), dt.minute())
        self.assertEqual(int(times[2]), int(dt.second()))
        self.assertEqual(dts[5], "%+03d%02d" % divmod( (-offset/60), 60) )


    def testInternationalDateformat(self):

        for year in range(1990, 2020):
            for month in range (1,13):
                for day in range(1,32):
                    try: d_us = DateTime("%d/%d/%d" % (year,month,day))
                    except: continue

                    d_int = DateTime("%d.%d.%d" % (day,month,year),
                                     datefmt="international")
                    self.assertEqual(d_us, d_int)

                    d_int = DateTime("%d/%d/%d" % (day,month,year),
                                     datefmt="international")
                    self.assertEqual(d_us, d_int)

    def test_calcTimezoneName(self):
        timezone_dependent_epoch = 2177452800L
        try:
            DateTime()._calcTimezoneName(timezone_dependent_epoch, 0)
        except DateTime.TimeError:
            self.fail('Zope Collector issue #484 (negative time bug): '
                      'TimeError raised')
    
    def testStrftimeTZhandling(self):
        '''strftime timezone testing'''
        # This is a test for collector issue #1127
        format = '%Y-%m-%d %H:%M %Z'
        dt = DateTime('Wed, 19 Nov 2003 18:32:07 -0215')
        dt_string = dt.strftime(format)
        dt_local = dt.toZone(_findLocalTimeZoneName(0))
        dt_localstring = dt_local.strftime(format)
        self.assertEqual(dt_string, dt_localstring)

    def testStrftimeFarDates(self):
        '''Checks strftime in dates <= 1900 or >= 2038'''
        dt = DateTime('1900/01/30')
        self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/1900')
        dt = DateTime('2040/01/30')
        self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/2040')

    def testZoneInFarDates(self):
        '''Checks time zone in dates <= 1900 or >= 2038'''
        dt1 = DateTime('2040/01/30 14:33 GMT+1')
        dt2 = DateTime('2040/01/30 11:33 GMT-2')
        self.assertEqual(dt1.strftime('%d/%m/%Y %H:%M'),
                         dt2.strftime('%d/%m/%Y %H:%M'))

    def testStrftimeUnicode(self):
        dt = DateTime('2002-05-02T08:00:00+00:00')
        ok = dt.strftime('Le %d/%m/%Y a %Hh%M').replace('a', u'\xe0')
        self.assertEqual(dt.strftime(u'Le %d/%m/%Y \xe0 %Hh%M'), ok)

def test_suite():
    from zope.testing import doctest
    return unittest.TestSuite([
        unittest.makeSuite(DateTimeTests),
        doctest.DocFileSuite('DateTime.txt', package='DateTime')
        ])

if __name__=="__main__":
    unittest.main(defaultTest='test_suite')
