-
diff --git a/trac/attachment.py b/trac/attachment.py
|
a
|
b
|
|
| 21 | 21 | import re |
| 22 | 22 | import shutil |
| 23 | 23 | import sys |
| 24 | | import time |
| 25 | 24 | import unicodedata |
| 26 | 25 | |
| 27 | 26 | from genshi.builder import tag |
| … |
… |
|
| 37 | 36 | from trac.resource import * |
| 38 | 37 | from trac.search import search_to_sql, shorten_result |
| 39 | 38 | from trac.util import get_reporter_id, create_unique_file |
| 40 | | from trac.util.datefmt import format_datetime, to_timestamp, utc |
| | 39 | from trac.util.datefmt import format_datetime, from_utimestamp, \ |
| | 40 | to_datetime, to_utimestamp, utc |
| 41 | 41 | from trac.util.text import exception_to_unicode, pretty_size, print_table, \ |
| 42 | 42 | unicode_quote, unicode_unquote |
| 43 | 43 | from trac.util.translation import _ |
| … |
… |
|
| 151 | 151 | self.filename = row[0] |
| 152 | 152 | self.description = row[1] |
| 153 | 153 | self.size = row[2] and int(row[2]) or 0 |
| 154 | | time = row[3] and int(row[3]) or 0 |
| 155 | | self.date = datetime.fromtimestamp(time, utc) |
| | 154 | self.date = from_utimestamp(row[3]) |
| 156 | 155 | self.author = row[4] |
| 157 | 156 | self.ipnr = row[5] |
| 158 | 157 | |
| … |
… |
|
| 195 | 194 | |
| 196 | 195 | |
| 197 | 196 | def insert(self, filename, fileobj, size, t=None, db=None): |
| 198 | | # FIXME: `t` should probably be switched to `datetime` too |
| 199 | | |
| 200 | 197 | self.size = size and int(size) or 0 |
| 201 | | timestamp = int(t or time.time()) |
| 202 | | self.date = datetime.fromtimestamp(timestamp, utc) |
| | 198 | if t is None: |
| | 199 | t = datetime.now(utc) |
| | 200 | elif not isinstance(t, datetime): # Compatibility with 0.11 |
| | 201 | t = to_datetime(t, utc) |
| | 202 | self.date = t |
| 203 | 203 | |
| 204 | 204 | # Make sure the path to the attachment is inside the environment |
| 205 | 205 | # attachments directory |
| … |
… |
|
| 225 | 225 | cursor.execute("INSERT INTO attachment " |
| 226 | 226 | "VALUES (%s,%s,%s,%s,%s,%s,%s,%s)", |
| 227 | 227 | (self.parent_realm, self.parent_id, filename, |
| 228 | | self.size, timestamp, self.description, |
| | 228 | self.size, to_utimestamp(t), self.description, |
| 229 | 229 | self.author, self.ipnr)) |
| 230 | 230 | shutil.copyfileobj(fileobj, targetfile) |
| 231 | 231 | self.resource.id = self.filename = filename |
| … |
… |
|
| 252 | 252 | attachment.filename = filename |
| 253 | 253 | attachment.description = description |
| 254 | 254 | attachment.size = size and int(size) or 0 |
| 255 | | time = time and int(time) or 0 |
| 256 | | attachment.date = datetime.fromtimestamp(time, utc) |
| | 255 | attachment.date = from_utimestamp(time or 0) |
| 257 | 256 | attachment.author = author |
| 258 | 257 | attachment.ipnr = ipnr |
| 259 | 258 | yield attachment |
| … |
… |
|
| 439 | 438 | " FROM attachment " |
| 440 | 439 | " WHERE time > %s AND time < %s " |
| 441 | 440 | " AND type = %s", |
| 442 | | (to_timestamp(start), to_timestamp(stop), realm)) |
| | 441 | (to_utimestamp(start), to_utimestamp(stop), realm)) |
| 443 | 442 | for realm, id, filename, ts, description, author in cursor: |
| 444 | | time = datetime.fromtimestamp(ts, utc) |
| | 443 | time = from_utimestamp(ts) |
| 445 | 444 | yield ('created', realm, id, filename, time, description, author) |
| 446 | 445 | |
| 447 | 446 | def get_timeline_events(self, req, resource_realm, start, stop): |
| … |
… |
|
| 489 | 488 | if 'ATTACHMENT_VIEW' in req.perm(attachment): |
| 490 | 489 | yield (get_resource_url(self.env, attachment, req.href), |
| 491 | 490 | get_resource_shortname(self.env, attachment), |
| 492 | | datetime.fromtimestamp(time, utc), author, |
| | 491 | from_utimestamp(time), author, |
| 493 | 492 | shorten_result(desc, terms)) |
| 494 | 493 | |
| 495 | 494 | # IResourceManager methods |
-
diff --git a/trac/db/api.py b/trac/db/api.py
|
a
|
b
|
|
| 83 | 83 | self._cnx_pool = None |
| 84 | 84 | |
| 85 | 85 | def init_db(self): |
| 86 | | connector, args = self._get_connector() |
| | 86 | connector, args = self.get_connector() |
| 87 | 87 | connector.init_db(**args) |
| 88 | 88 | |
| 89 | 89 | def get_connection(self): |
| 90 | 90 | if not self._cnx_pool: |
| 91 | | connector, args = self._get_connector() |
| | 91 | connector, args = self.get_connector() |
| 92 | 92 | self._cnx_pool = ConnectionPool(5, connector, **args) |
| 93 | 93 | return self._cnx_pool.get_cnx(self.timeout or None) |
| 94 | 94 | |
| … |
… |
|
| 104 | 104 | @param dest: base filename to write to. |
| 105 | 105 | Returns the file actually written. |
| 106 | 106 | """ |
| 107 | | connector, args = self._get_connector() |
| | 107 | connector, args = self.get_connector() |
| 108 | 108 | if not dest: |
| 109 | 109 | backup_dir = self.backup_dir |
| 110 | 110 | if backup_dir[0] != "/": |
| … |
… |
|
| 120 | 120 | os.makedirs(backup_dir) |
| 121 | 121 | return connector.backup(dest) |
| 122 | 122 | |
| 123 | | def _get_connector(self): ### FIXME: Make it public? |
| | 123 | def get_connector(self): |
| 124 | 124 | scheme, args = _parse_db_str(self.connection_uri) |
| 125 | 125 | candidates = [ |
| 126 | 126 | (priority, connector) |
| … |
… |
|
| 147 | 147 | args['log'] = self.log |
| 148 | 148 | return connector, args |
| 149 | 149 | |
| | 150 | _get_connector = get_connector # For 0.11 compatibility |
| | 151 | |
| 150 | 152 | |
| 151 | 153 | def _parse_db_str(db_str): |
| 152 | 154 | scheme, rest = db_str.split(':', 1) |
-
diff --git a/trac/db/mysql_backend.py b/trac/db/mysql_backend.py
|
a
|
b
|
|
| 52 | 52 | except ImportError: |
| 53 | 53 | has_mysqldb = False |
| 54 | 54 | |
| | 55 | # Mapping from "abstract" SQL types to DB-specific types |
| | 56 | _type_map = { |
| | 57 | 'int64': 'bigint', |
| | 58 | } |
| | 59 | |
| 55 | 60 | |
| 56 | 61 | class MySQLConnector(Component): |
| 57 | 62 | """Database connector for MySQL version 4.1 and greater. |
| … |
… |
|
| 134 | 139 | coldefs = [] |
| 135 | 140 | for column in table.columns: |
| 136 | 141 | ctype = column.type |
| | 142 | ctype = _type_map.get(ctype, ctype) |
| 137 | 143 | if column.auto_increment: |
| 138 | 144 | ctype = 'INT UNSIGNED NOT NULL AUTO_INCREMENT' |
| 139 | 145 | # Override the column type, as a text field cannot |
| … |
… |
|
| 152 | 158 | '_'.join(index.columns), table.name, |
| 153 | 159 | self._collist(table, index.columns)) |
| 154 | 160 | |
| | 161 | def alter_column_types(self, table, columns): |
| | 162 | """Yield SQL statements altering the type of one or more columns of |
| | 163 | a table. |
| | 164 | |
| | 165 | Type changes are specified as a `columns` dict mapping column names |
| | 166 | to `(from, to)` SQL type tuples. |
| | 167 | """ |
| | 168 | alterations = [] |
| | 169 | for name, (from_, to) in sorted(columns.iteritems()): |
| | 170 | to = _type_map.get(to, to) |
| | 171 | if to != _type_map.get(from_, from_): |
| | 172 | alterations.append((name, to)) |
| | 173 | if alterations: |
| | 174 | yield "ALTER TABLE %s %s" % (table, |
| | 175 | ', '.join("MODIFY %s %s" % each |
| | 176 | for each in alterations)) |
| | 177 | |
| 155 | 178 | def backup(self, dest_file): |
| 156 | 179 | try: |
| 157 | 180 | from subprocess import Popen, PIPE |
| … |
… |
|
| 204 | 227 | self._is_closed = False |
| 205 | 228 | |
| 206 | 229 | def cast(self, column, type): |
| 207 | | if type == 'int': |
| | 230 | if type == 'int'or type == 'int64': |
| 208 | 231 | type = 'signed' |
| 209 | 232 | elif type == 'text': |
| 210 | 233 | type = 'char' |
-
diff --git a/trac/db/postgres_backend.py b/trac/db/postgres_backend.py
|
a
|
b
|
|
| 45 | 45 | |
| 46 | 46 | _like_escape_re = re.compile(r'([/_%])') |
| 47 | 47 | |
| | 48 | # Mapping from "abstract" SQL types to DB-specific types |
| | 49 | _type_map = { |
| | 50 | 'int64': 'bigint', |
| | 51 | } |
| | 52 | |
| 48 | 53 | |
| 49 | 54 | class PostgreSQLConnector(Component): |
| 50 | 55 | """Database connector for PostgreSQL. |
| … |
… |
|
| 97 | 102 | coldefs = [] |
| 98 | 103 | for column in table.columns: |
| 99 | 104 | ctype = column.type |
| | 105 | ctype = _type_map.get(ctype, ctype) |
| 100 | 106 | if column.auto_increment: |
| 101 | 107 | ctype = 'SERIAL' |
| 102 | | if len(table.key) == 1 and column.name in table.key: |
| | 108 | elif len(table.key) == 1 and column.name in table.key: |
| 103 | 109 | ctype += ' PRIMARY KEY' |
| 104 | 110 | coldefs.append(' "%s" %s' % (column.name, ctype)) |
| 105 | 111 | if len(table.key) > 1: |
| … |
… |
|
| 114 | 120 | '_'.join(index.columns), table.name, |
| 115 | 121 | '","'.join(index.columns)) |
| 116 | 122 | |
| | 123 | def alter_column_types(self, table, columns): |
| | 124 | """Yield SQL statements altering the type of one or more columns of |
| | 125 | a table. |
| | 126 | |
| | 127 | Type changes are specified as a `columns` dict mapping column names |
| | 128 | to `(from, to)` SQL type tuples. |
| | 129 | """ |
| | 130 | alterations = [] |
| | 131 | for name, (from_, to) in sorted(columns.iteritems()): |
| | 132 | to = _type_map.get(to, to) |
| | 133 | if to != _type_map.get(from_, from_): |
| | 134 | alterations.append((name, to)) |
| | 135 | if alterations: |
| | 136 | yield "ALTER TABLE %s %s" % (table, |
| | 137 | ', '.join("ALTER COLUMN %s TYPE %s" % each |
| | 138 | for each in alterations)) |
| | 139 | |
| 117 | 140 | def backup(self, dest_file): |
| 118 | 141 | try: |
| 119 | 142 | from subprocess import Popen, PIPE |
| … |
… |
|
| 191 | 214 | |
| 192 | 215 | def cast(self, column, type): |
| 193 | 216 | # Temporary hack needed for the union of selects in the search module |
| 194 | | return 'CAST(%s AS %s)' % (column, type) |
| | 217 | return 'CAST(%s AS %s)' % (column, _type_map.get(type, type)) |
| 195 | 218 | |
| 196 | 219 | def concat(self, *args): |
| 197 | 220 | return '||'.join(args) |
-
diff --git a/trac/db/sqlite_backend.py b/trac/db/sqlite_backend.py
|
a
|
b
|
|
| 101 | 101 | return result |
| 102 | 102 | |
| 103 | 103 | |
| | 104 | # Mapping from "abstract" SQL types to DB-specific types |
| | 105 | _type_map = { |
| | 106 | 'int': 'integer', |
| | 107 | 'int64': 'integer', |
| | 108 | } |
| | 109 | |
| | 110 | |
| 104 | 111 | def _to_sql(table): |
| 105 | 112 | sql = ["CREATE TABLE %s (" % table.name] |
| 106 | 113 | coldefs = [] |
| 107 | 114 | for column in table.columns: |
| 108 | 115 | ctype = column.type.lower() |
| | 116 | ctype = _type_map.get(ctype, ctype) |
| 109 | 117 | if column.auto_increment: |
| 110 | 118 | ctype = "integer PRIMARY KEY" |
| 111 | 119 | elif len(table.key) == 1 and column.name in table.key: |
| 112 | 120 | ctype += " PRIMARY KEY" |
| 113 | | elif ctype == "int": |
| 114 | | ctype = "integer" |
| 115 | 121 | coldefs.append(" %s %s" % (column.name, ctype)) |
| 116 | 122 | if len(table.key) > 1: |
| 117 | 123 | coldefs.append(" UNIQUE (%s)" % ','.join(table.key)) |
| … |
… |
|
| 187 | 193 | def to_sql(self, table): |
| 188 | 194 | return _to_sql(table) |
| 189 | 195 | |
| | 196 | def alter_column_types(self, table, columns): |
| | 197 | """Yield SQL statements altering the type of one or more columns of |
| | 198 | a table. |
| | 199 | |
| | 200 | Type changes are specified as a `columns` dict mapping column names |
| | 201 | to `(from, to)` SQL type tuples. |
| | 202 | """ |
| | 203 | for name, (from_, to) in sorted(columns.iteritems()): |
| | 204 | if _type_map.get(to, to) != _type_map.get(from_, from_): |
| | 205 | raise NotImplementedError('Conversion from %s to %s is not ' |
| | 206 | 'implemented' % (from_, to)) |
| | 207 | return () |
| | 208 | |
| 190 | 209 | def backup(self, dest_file): |
| 191 | 210 | """Simple SQLite-specific backup of the database. |
| 192 | 211 | |
| … |
… |
|
| 204 | 223 | raise TracError("Backup attempt failed") |
| 205 | 224 | return dest_file |
| 206 | 225 | |
| | 226 | |
| 207 | 227 | class SQLiteConnection(ConnectionWrapper): |
| 208 | 228 | """Connection wrapper for SQLite.""" |
| 209 | 229 | |
| … |
… |
|
| 260 | 280 | |
| 261 | 281 | def cast(self, column, type): |
| 262 | 282 | if sqlite_version >= 30203: |
| 263 | | return 'CAST(%s AS %s)' % (column, type) |
| | 283 | return 'CAST(%s AS %s)' % (column, _type_map.get(type, type)) |
| 264 | 284 | elif type == 'int': |
| 265 | 285 | # hack to force older SQLite versions to convert column to an int |
| 266 | 286 | return '1*' + column |
-
diff --git a/trac/db/tests/__init__.py b/trac/db/tests/__init__.py
|
a
|
b
|
|
| 1 | 1 | import unittest |
| 2 | 2 | |
| 3 | | from trac.db.tests import api |
| 4 | | from trac.db.tests import postgres_test |
| | 3 | from trac.db.tests import api, mysql_test, postgres_test |
| 5 | 4 | |
| 6 | 5 | from trac.db.tests.functional import functionalSuite |
| 7 | 6 | |
| … |
… |
|
| 9 | 8 | |
| 10 | 9 | suite = unittest.TestSuite() |
| 11 | 10 | suite.addTest(api.suite()) |
| | 11 | suite.addTest(mysql_test.suite()) |
| 12 | 12 | suite.addTest(postgres_test.suite()) |
| 13 | 13 | return suite |
| 14 | 14 | |
-
diff --git a/trac/db/tests/mysql_test.py b/trac/db/tests/mysql_test.py
new file mode 100644
|
-
|
+
|
|
| | 1 | # -*- coding: utf-8 -*- |
| | 2 | # |
| | 3 | # Copyright (C) 2009 Edgewall Software |
| | 4 | # All rights reserved. |
| | 5 | # |
| | 6 | # This software is licensed as described in the file COPYING, which |
| | 7 | # you should have received as part of this distribution. The terms |
| | 8 | # are also available at http://trac.edgewall.org/wiki/TracLicense. |
| | 9 | # |
| | 10 | # This software consists of voluntary contributions made by many |
| | 11 | # individuals. For the exact contribution history, see the revision |
| | 12 | # history and logs, available at http://trac.edgewall.org/log/. |
| | 13 | |
| | 14 | import unittest |
| | 15 | |
| | 16 | from trac.db.mysql_backend import MySQLConnector |
| | 17 | from trac.test import EnvironmentStub |
| | 18 | |
| | 19 | |
| | 20 | class MySQLTableAlterationSQLTest(unittest.TestCase): |
| | 21 | def setUp(self): |
| | 22 | self.env = EnvironmentStub() |
| | 23 | |
| | 24 | def test_alter_column_types(self): |
| | 25 | connector = MySQLConnector(self.env) |
| | 26 | sql = connector.alter_column_types('milestone', |
| | 27 | {'due': ('int', 'int64'), |
| | 28 | 'completed': ('int', 'int64')}) |
| | 29 | sql = list(sql) |
| | 30 | self.assertEqual([ |
| | 31 | "ALTER TABLE milestone " |
| | 32 | "MODIFY completed bigint, " |
| | 33 | "MODIFY due bigint", |
| | 34 | ], sql) |
| | 35 | |
| | 36 | def test_alter_column_types_same(self): |
| | 37 | connector = MySQLConnector(self.env) |
| | 38 | sql = connector.alter_column_types('milestone', |
| | 39 | {'due': ('int', 'int'), |
| | 40 | 'completed': ('int', 'int64')}) |
| | 41 | sql = list(sql) |
| | 42 | self.assertEqual([ |
| | 43 | "ALTER TABLE milestone " |
| | 44 | "MODIFY completed bigint", |
| | 45 | ], sql) |
| | 46 | |
| | 47 | def test_alter_column_types_none(self): |
| | 48 | connector = MySQLConnector(self.env) |
| | 49 | sql = connector.alter_column_types('milestone', |
| | 50 | {'due': ('int', 'int')}) |
| | 51 | self.assertEqual([], list(sql)) |
| | 52 | |
| | 53 | |
| | 54 | def suite(): |
| | 55 | suite = unittest.TestSuite() |
| | 56 | suite.addTest(unittest.makeSuite(MySQLTableAlterationSQLTest, 'test')) |
| | 57 | return suite |
| | 58 | |
| | 59 | |
| | 60 | if __name__ == '__main__': |
| | 61 | unittest.main(defaultTest='suite') |
-
diff --git a/trac/db/tests/postgres_test.py b/trac/db/tests/postgres_test.py
|
a
|
b
|
|
| 11 | 11 | class PostgresTableCreationSQLTest(unittest.TestCase): |
| 12 | 12 | def setUp(self): |
| 13 | 13 | self.env = EnvironmentStub() |
| 14 | | self.db = self.env.get_db_cnx() |
| 15 | 14 | |
| 16 | 15 | def _unroll_generator(self, generator): |
| 17 | 16 | items = [] |
| … |
… |
|
| 82 | 81 | self.assertEqual(index_sql, sql_commands[1]) |
| 83 | 82 | |
| 84 | 83 | |
| | 84 | class PostgresTableAlterationSQLTest(unittest.TestCase): |
| | 85 | def setUp(self): |
| | 86 | self.env = EnvironmentStub() |
| | 87 | |
| | 88 | def test_alter_column_types(self): |
| | 89 | connector = PostgreSQLConnector(self.env) |
| | 90 | sql = connector.alter_column_types('milestone', |
| | 91 | {'due': ('int', 'int64'), |
| | 92 | 'completed': ('int', 'int64')}) |
| | 93 | sql = list(sql) |
| | 94 | self.assertEqual([ |
| | 95 | "ALTER TABLE milestone " |
| | 96 | "ALTER COLUMN completed TYPE bigint, " |
| | 97 | "ALTER COLUMN due TYPE bigint", |
| | 98 | ], sql) |
| | 99 | |
| | 100 | def test_alter_column_types_same(self): |
| | 101 | connector = PostgreSQLConnector(self.env) |
| | 102 | sql = connector.alter_column_types('milestone', |
| | 103 | {'due': ('int', 'int'), |
| | 104 | 'completed': ('int', 'int64')}) |
| | 105 | sql = list(sql) |
| | 106 | self.assertEqual([ |
| | 107 | "ALTER TABLE milestone " |
| | 108 | "ALTER COLUMN completed TYPE bigint", |
| | 109 | ], sql) |
| | 110 | |
| | 111 | def test_alter_column_types_none(self): |
| | 112 | connector = PostgreSQLConnector(self.env) |
| | 113 | sql = connector.alter_column_types('milestone', |
| | 114 | {'due': ('int', 'int')}) |
| | 115 | self.assertEqual([], list(sql)) |
| | 116 | |
| | 117 | |
| 85 | 118 | def suite(): |
| 86 | | return unittest.makeSuite(PostgresTableCreationSQLTest, 'test') |
| | 119 | suite = unittest.TestSuite() |
| | 120 | suite.addTest(unittest.makeSuite(PostgresTableCreationSQLTest, 'test')) |
| | 121 | suite.addTest(unittest.makeSuite(PostgresTableAlterationSQLTest, 'test')) |
| | 122 | return suite |
| | 123 | |
| 87 | 124 | |
| 88 | 125 | if __name__ == '__main__': |
| 89 | 126 | unittest.main(defaultTest='suite') |
-
diff --git a/trac/db_default.py b/trac/db_default.py
|
a
|
b
|
|
| 17 | 17 | from trac.db import Table, Column, Index |
| 18 | 18 | |
| 19 | 19 | # Database version identifier. Used for automatic upgrades. |
| 20 | | db_version = 24 |
| | 20 | db_version = 25 |
| 21 | 21 | |
| 22 | 22 | def __mkreports(reports): |
| 23 | 23 | """Utility function used to create report data in same syntax as the |
| … |
… |
|
| 67 | 67 | Column('id'), |
| 68 | 68 | Column('filename'), |
| 69 | 69 | Column('size', type='int'), |
| 70 | | Column('time', type='int'), |
| | 70 | Column('time', type='int64'), |
| 71 | 71 | Column('description'), |
| 72 | 72 | Column('author'), |
| 73 | 73 | Column('ipnr')], |
| … |
… |
|
| 76 | 76 | Table('wiki', key=('name', 'version'))[ |
| 77 | 77 | Column('name'), |
| 78 | 78 | Column('version', type='int'), |
| 79 | | Column('time', type='int'), |
| | 79 | Column('time', type='int64'), |
| 80 | 80 | Column('author'), |
| 81 | 81 | Column('ipnr'), |
| 82 | 82 | Column('text'), |
| … |
… |
|
| 92 | 92 | Table('revision', key=('repos', 'rev'))[ |
| 93 | 93 | Column('repos', type='int'), |
| 94 | 94 | Column('rev'), |
| 95 | | Column('time', type='int'), |
| | 95 | Column('time', type='int64'), |
| 96 | 96 | Column('author'), |
| 97 | 97 | Column('message'), |
| 98 | 98 | Index(['repos', 'time'])], |
| … |
… |
|
| 110 | 110 | Table('ticket', key='id')[ |
| 111 | 111 | Column('id', auto_increment=True), |
| 112 | 112 | Column('type'), |
| 113 | | Column('time', type='int'), |
| 114 | | Column('changetime', type='int'), |
| | 113 | Column('time', type='int64'), |
| | 114 | Column('changetime', type='int64'), |
| 115 | 115 | Column('component'), |
| 116 | 116 | Column('severity'), |
| 117 | 117 | Column('priority'), |
| … |
… |
|
| 129 | 129 | Index(['status'])], |
| 130 | 130 | Table('ticket_change', key=('ticket', 'time', 'field'))[ |
| 131 | 131 | Column('ticket', type='int'), |
| 132 | | Column('time', type='int'), |
| | 132 | Column('time', type='int64'), |
| 133 | 133 | Column('author'), |
| 134 | 134 | Column('field'), |
| 135 | 135 | Column('oldvalue'), |
| … |
… |
|
| 150 | 150 | Column('description')], |
| 151 | 151 | Table('milestone', key='name')[ |
| 152 | 152 | Column('name'), |
| 153 | | Column('due', type='int'), |
| 154 | | Column('completed', type='int'), |
| | 153 | Column('due', type='int64'), |
| | 154 | Column('completed', type='int64'), |
| 155 | 155 | Column('description')], |
| 156 | 156 | Table('version', key='name')[ |
| 157 | 157 | Column('name'), |
| 158 | | Column('time', type='int'), |
| | 158 | Column('time', type='int64'), |
| 159 | 159 | Column('description')], |
| 160 | 160 | |
| 161 | 161 | # Report system |
-
diff --git a/trac/ticket/model.py b/trac/ticket/model.py
|
a
|
b
|
|
| 27 | 27 | from trac.ticket.api import TicketSystem |
| 28 | 28 | from trac.util import embedded_numbers, partition |
| 29 | 29 | from trac.util.text import empty |
| 30 | | from trac.util.datefmt import utc, utcmax, to_timestamp |
| | 30 | from trac.util.datefmt import from_utimestamp, to_utimestamp, utc, utcmax |
| 31 | 31 | from trac.util.translation import _ |
| 32 | 32 | |
| 33 | 33 | __all__ = ['Ticket', 'Type', 'Status', 'Resolution', 'Priority', 'Severity', |
| … |
… |
|
| 109 | 109 | for i, field in enumerate(std_fields): |
| 110 | 110 | value = row[i] |
| 111 | 111 | if field in self.time_fields: |
| 112 | | self.values[field] = datetime.fromtimestamp(value or 0, utc) |
| | 112 | self.values[field] = from_utimestamp(value) |
| 113 | 113 | elif value is None: |
| 114 | 114 | self.values[field] = empty |
| 115 | 115 | else: |
| … |
… |
|
| 193 | 193 | values = dict(self.values) |
| 194 | 194 | for field in self.time_fields: |
| 195 | 195 | if field in values: |
| 196 | | values[field] = to_timestamp(values[field]) |
| | 196 | values[field] = to_utimestamp(values[field]) |
| 197 | 197 | |
| 198 | 198 | # Insert ticket record |
| 199 | 199 | std_fields = [] |
| … |
… |
|
| 245 | 245 | |
| 246 | 246 | if when is None: |
| 247 | 247 | when = datetime.now(utc) |
| 248 | | when_ts = to_timestamp(when) |
| | 248 | when_ts = to_utimestamp(when) |
| 249 | 249 | |
| 250 | 250 | if 'component' in self.values: |
| 251 | 251 | # If the component is changed on a 'new' ticket |
| … |
… |
|
| 359 | 359 | db = self._get_db(db) |
| 360 | 360 | cursor = db.cursor() |
| 361 | 361 | sid = str(self.id) |
| 362 | | when_ts = when and to_timestamp(when) or 0 |
| | 362 | when_ts = to_utimestamp(when) |
| 363 | 363 | if when_ts: |
| 364 | 364 | cursor.execute("SELECT time,author,field,oldvalue,newvalue," |
| 365 | 365 | "1 AS permanent FROM ticket_change " |
| … |
… |
|
| 387 | 387 | (self.id, sid, sid)) |
| 388 | 388 | log = [] |
| 389 | 389 | for t, author, field, oldvalue, newvalue, permanent in cursor: |
| 390 | | log.append((datetime.fromtimestamp(int(t), utc), author, field, |
| 391 | | oldvalue or '', newvalue or '', permanent)) |
| | 390 | log.append((from_utimestamp(t), author, field, |
| | 391 | oldvalue or '', newvalue or '', permanent)) |
| 392 | 392 | return log |
| 393 | 393 | |
| 394 | 394 | def delete(self, db=None): |
| … |
… |
|
| 417 | 417 | "WHERE ticket=%s AND time=%s", |
| 418 | 418 | (self.id, ts)) |
| 419 | 419 | fields = {} |
| 420 | | change = {'date': datetime.fromtimestamp(int(ts), utc), |
| | 420 | change = {'date': from_utimestamp(ts), |
| 421 | 421 | 'author': author, 'fields': fields} |
| 422 | 422 | for field, author, old, new in cursor: |
| 423 | 423 | fields[field] = {'author': author, 'old': old, 'new': new} |
| … |
… |
|
| 427 | 427 | """Modify a ticket comment specified by its date, while keeping a |
| 428 | 428 | history of edits. |
| 429 | 429 | """ |
| 430 | | ts = to_timestamp(cdate) |
| | 430 | ts = to_utimestamp(cdate) |
| 431 | 431 | if when is None: |
| 432 | 432 | when = datetime.now(utc) |
| 433 | | when_ts = to_timestamp(when) |
| | 433 | when_ts = to_utimestamp(when) |
| 434 | 434 | |
| 435 | 435 | @with_transaction(self.env, db) |
| 436 | 436 | def do_modify(db): |
| … |
… |
|
| 494 | 494 | rows = sorted((int(field[8:]), author, old, new) |
| 495 | 495 | for field, author, old, new in cursor) |
| 496 | 496 | for rev, author, comment, ts in rows: |
| 497 | | history.append((rev, datetime.fromtimestamp(int(ts0), utc), |
| 498 | | author0, comment)) |
| | 497 | history.append((rev, from_utimestamp(long(ts0)), author0, |
| | 498 | comment)) |
| 499 | 499 | ts0, author0 = ts, author |
| 500 | 500 | history.sort() |
| 501 | 501 | rev = history and (history[-1][0] + 1) or 0 |
| 502 | | history.append((rev, datetime.fromtimestamp(int(ts0), utc), |
| 503 | | author0, last_comment)) |
| | 502 | history.append((rev, from_utimestamp(long(ts0)), author0, |
| | 503 | last_comment)) |
| 504 | 504 | return history |
| 505 | 505 | |
| 506 | 506 | def _find_comment(self, cnum, db): |
| … |
… |
|
| 823 | 823 | def _from_database(self, row): |
| 824 | 824 | name, due, completed, description = row |
| 825 | 825 | self.name = name |
| 826 | | self.due = due and datetime.fromtimestamp(int(due), utc) or None |
| 827 | | self.completed = completed and \ |
| 828 | | datetime.fromtimestamp(int(completed), utc) or None |
| | 826 | self.due = due and from_utimestamp(due) or None |
| | 827 | self.completed = completed and from_utimestamp(completed) or None |
| 829 | 828 | self.description = description or '' |
| 830 | 829 | self._to_old() |
| 831 | 830 | |
| … |
… |
|
| 869 | 868 | cursor.execute("INSERT INTO milestone " |
| 870 | 869 | "(name,due,completed,description) " |
| 871 | 870 | "VALUES (%s,%s,%s,%s)", |
| 872 | | (self.name, to_timestamp(self.due), |
| 873 | | to_timestamp(self.completed), self.description)) |
| | 871 | (self.name, to_utimestamp(self.due), |
| | 872 | to_utimestamp(self.completed), self.description)) |
| 874 | 873 | self._to_old() |
| 875 | 874 | TicketSystem(self.env).reset_ticket_fields(db) |
| 876 | 875 | |
| … |
… |
|
| 888 | 887 | self.env.log.info('Updating milestone "%s"' % self.name) |
| 889 | 888 | cursor.execute("UPDATE milestone SET name=%s,due=%s," |
| 890 | 889 | "completed=%s,description=%s WHERE name=%s", |
| 891 | | (self.name, to_timestamp(self.due), |
| 892 | | to_timestamp(self.completed), |
| | 890 | (self.name, to_utimestamp(self.due), |
| | 891 | to_utimestamp(self.completed), |
| 893 | 892 | self.description, self._old['name'])) |
| 894 | 893 | self.env.log.info('Updating milestone field of all tickets ' |
| 895 | 894 | 'associated with milestone "%s"' % self.name) |
| … |
… |
|
| 957 | 956 | raise ResourceNotFound(_('Version %(name)s does not exist.', |
| 958 | 957 | name=name)) |
| 959 | 958 | self.name = self._old_name = name |
| 960 | | self.time = row[0] and datetime.fromtimestamp(int(row[0]), utc) \ |
| 961 | | or None |
| | 959 | self.time = row[0] and from_utimestamp(row[0]) or None |
| 962 | 960 | self.description = row[1] or '' |
| 963 | 961 | else: |
| 964 | 962 | self.name = self._old_name = None |
| … |
… |
|
| 990 | 988 | self.env.log.debug("Creating new version '%s'" % self.name) |
| 991 | 989 | cursor.execute("INSERT INTO version (name,time,description) " |
| 992 | 990 | "VALUES (%s,%s,%s)", |
| 993 | | (self.name, to_timestamp(self.time), |
| | 991 | (self.name, to_utimestamp(self.time), |
| 994 | 992 | self.description)) |
| 995 | 993 | self._old_name = self.name |
| 996 | 994 | TicketSystem(self.env).reset_ticket_fields(db) |
| … |
… |
|
| 1007 | 1005 | self.env.log.info('Updating version "%s"' % self.name) |
| 1008 | 1006 | cursor.execute("UPDATE version SET name=%s,time=%s,description=%s " |
| 1009 | 1007 | "WHERE name=%s", |
| 1010 | | (self.name, to_timestamp(self.time), |
| | 1008 | (self.name, to_utimestamp(self.time), |
| 1011 | 1009 | self.description, self._old_name)) |
| 1012 | 1010 | if self.name != self._old_name: |
| 1013 | 1011 | # Update tickets |
| … |
… |
|
| 1026 | 1024 | for name, time, description in cursor: |
| 1027 | 1025 | version = cls(env) |
| 1028 | 1026 | version.name = version._old_name = name |
| 1029 | | version.time = time and datetime.fromtimestamp(int(time), utc) \ |
| 1030 | | or None |
| | 1027 | version.time = time and from_utimestamp(time) or None |
| 1031 | 1028 | version.description = description or '' |
| 1032 | 1029 | versions.append(version) |
| 1033 | 1030 | def version_order(v): |
-
diff --git a/trac/ticket/query.py b/trac/ticket/query.py
|
a
|
b
|
|
| 31 | 31 | from trac.resource import Resource |
| 32 | 32 | from trac.ticket.api import TicketSystem |
| 33 | 33 | from trac.util import Ranges |
| 34 | | from trac.util.datefmt import format_datetime, parse_date, to_timestamp, utc |
| | 34 | from trac.util.datefmt import format_datetime, from_utimestamp, parse_date, \ |
| | 35 | to_timestamp, to_utimestamp, utc |
| 35 | 36 | from trac.util.presentation import Paginator |
| 36 | 37 | from trac.util.text import empty, shorten_line, unicode_unquote |
| 37 | 38 | from trac.util.translation import _, tag_ |
| … |
… |
|
| 331 | 332 | elif val is None: |
| 332 | 333 | val = '--' |
| 333 | 334 | elif name in self.time_fields: |
| 334 | | val = datetime.fromtimestamp(int(val or 0), utc) |
| | 335 | val = from_utimestamp(val) |
| 335 | 336 | elif field and field['type'] == 'checkbox': |
| 336 | 337 | try: |
| 337 | 338 | val = bool(int(val)) |
| … |
… |
|
| 468 | 469 | def get_timestamp(date): |
| 469 | 470 | if date: |
| 470 | 471 | try: |
| 471 | | return to_timestamp(parse_date(date, req.tz)) |
| | 472 | return to_utimestamp(parse_date(date, req.tz)) |
| 472 | 473 | except TracError, e: |
| 473 | 474 | errors.append(unicode(e)) |
| 474 | 475 | return None |
| … |
… |
|
| 486 | 487 | value.split(';', 1)] |
| 487 | 488 | else: |
| 488 | 489 | (start, end) = (value.strip(), '') |
| 489 | | col_cast = db.cast(col, 'int') |
| | 490 | col_cast = db.cast(col, 'int64') |
| 490 | 491 | start = get_timestamp(start) |
| 491 | 492 | end = get_timestamp(end) |
| 492 | 493 | if start is not None and end is not None: |
-
diff --git a/trac/ticket/roadmap.py b/trac/ticket/roadmap.py
|
a
|
b
|
|
| 18 | 18 | from StringIO import StringIO |
| 19 | 19 | from datetime import datetime |
| 20 | 20 | import re |
| 21 | | from time import time |
| 22 | 21 | |
| 23 | 22 | from genshi.builder import tag |
| 24 | 23 | |
| … |
… |
|
| 31 | 30 | from trac.perm import IPermissionRequestor |
| 32 | 31 | from trac.resource import * |
| 33 | 32 | from trac.search import ISearchSource, search_to_sql, shorten_result |
| 34 | | from trac.util.datefmt import parse_date, utc, to_timestamp, to_datetime, \ |
| | 33 | from trac.util.datefmt import parse_date, utc, to_utimestamp, \ |
| 35 | 34 | get_date_format_hint, get_datetime_format_hint, \ |
| 36 | | format_date, format_datetime |
| | 35 | format_date, format_datetime, from_utimestamp |
| 37 | 36 | from trac.util.text import CRLF |
| 38 | 37 | from trac.util.translation import _ |
| 39 | 38 | from trac.ticket import Milestone, Ticket, TicketSystem, group_milestones |
| … |
… |
|
| 476 | 475 | (ticket.id,)) |
| 477 | 476 | row = cursor.fetchone() |
| 478 | 477 | if row: |
| 479 | | write_utctime('COMPLETED', to_datetime(row[0], utc)) |
| | 478 | write_utctime('COMPLETED', from_utimestamp(row[0])) |
| 480 | 479 | write_prop('END', 'VTODO') |
| 481 | 480 | write_prop('END', 'VCALENDAR') |
| 482 | 481 | |
| … |
… |
|
| 531 | 530 | # TODO: creation and (later) modifications should also be reported |
| 532 | 531 | cursor.execute("SELECT completed,name,description FROM milestone " |
| 533 | 532 | "WHERE completed>=%s AND completed<=%s", |
| 534 | | (to_timestamp(start), to_timestamp(stop))) |
| | 533 | (to_utimestamp(start), to_utimestamp(stop))) |
| 535 | 534 | for completed, name, description in cursor: |
| 536 | 535 | milestone = milestone_realm(id=name) |
| 537 | 536 | if 'MILESTONE_VIEW' in req.perm(milestone): |
| 538 | | yield('milestone', datetime.fromtimestamp(completed, utc), |
| | 537 | yield('milestone', from_utimestamp(completed), |
| 539 | 538 | '', (milestone, description)) # FIXME: author? |
| 540 | 539 | |
| 541 | 540 | # Attachments |
| … |
… |
|
| 866 | 865 | for name, due, completed, description in cursor: |
| 867 | 866 | milestone = milestone_realm(id=name) |
| 868 | 867 | if 'MILESTONE_VIEW' in req.perm(milestone): |
| | 868 | dt = (completed and from_utimestamp(completed) or |
| | 869 | due and from_utimestamp(due) or datetime.now(utc)) |
| 869 | 870 | yield (get_resource_url(self.env, milestone, req.href), |
| 870 | | get_resource_name(self.env, milestone), |
| 871 | | datetime.fromtimestamp( |
| 872 | | completed or due or time(), utc), |
| | 871 | get_resource_name(self.env, milestone), dt, |
| 873 | 872 | '', shorten_result(description, terms)) |
| 874 | 873 | |
| 875 | 874 | # Attachments |
-
diff --git a/trac/ticket/templates/report.rss b/trac/ticket/templates/report.rss
|
a
|
b
|
|
| 26 | 26 | </py:when> |
| 27 | 27 | <py:when test="col in ('time', 'changetime', 'created', 'modified')"> |
| 28 | 28 | <!-- FIXME: we end up with multiple pubDate --> |
| 29 | | <pubDate py:if="cell.value != 'None'">${http_date(fromtimestamp(int(cell.value)))}</pubDate> |
| | 29 | <pubDate py:if="cell.value != 'None'">${http_date(from_utimestamp(long(cell.value)))}</pubDate> |
| 30 | 30 | </py:when> |
| 31 | 31 | <py:when test="col == 'summary'"> |
| 32 | 32 | <title>#$row.id: $cell.value</title> |
-
diff --git a/trac/ticket/templates/report_view.html b/trac/ticket/templates/report_view.html
|
a
|
b
|
|
| 147 | 147 | |
| 148 | 148 | <!--! generic fields --> |
| 149 | 149 | <py:when test="col == 'time'"> |
| 150 | | <td class="date" py:attrs="td_attrs">${cell.value != '' and format_time(int(cell.value)) or '--'} |
| | 150 | <td class="date" py:attrs="td_attrs">${cell.value != '' and format_time(from_utimestamp(long(cell.value))) or '--'} |
| 151 | 151 | <hr py:if="fullrow"/> |
| 152 | 152 | </td> |
| 153 | 153 | </py:when> |
| 154 | 154 | |
| 155 | 155 | <py:when test="col in ('date', 'created', 'modified')"> |
| 156 | | <td class="date" py:attrs="td_attrs">${cell.value != '' and format_date(int(cell.value)) or '--'} |
| | 156 | <td class="date" py:attrs="td_attrs">${cell.value != '' and format_date(from_utimestamp(long(cell.value))) or '--'} |
| 157 | 157 | <hr py:if="fullrow"/> |
| 158 | 158 | </td> |
| 159 | 159 | </py:when> |
| 160 | 160 | |
| 161 | 161 | <py:when test="col == 'datetime'"> |
| 162 | | <td class="date" py:attrs="td_attrs">${cell.value != '' and format_datetime(int(cell.value)) or '--'} |
| | 162 | <td class="date" py:attrs="td_attrs">${cell.value != '' and format_datetime(from_utimestamp(long(cell.value))) or '--'} |
| 163 | 163 | <hr py:if="fullrow"/> |
| 164 | 164 | </td> |
| 165 | 165 | </py:when> |
-
diff --git a/trac/ticket/tests/model.py b/trac/ticket/tests/model.py
|
a
|
b
|
|
| 4 | 4 | from trac.ticket.model import Ticket, Component, Milestone, Priority, Type, Version |
| 5 | 5 | from trac.ticket.api import IMilestoneChangeListener, ITicketChangeListener |
| 6 | 6 | from trac.test import EnvironmentStub |
| 7 | | from trac.util.datefmt import utc, to_timestamp |
| | 7 | from trac.util.datefmt import from_utimestamp, to_utimestamp, utc |
| 8 | 8 | |
| 9 | 9 | from datetime import datetime, timedelta |
| 10 | 10 | import unittest |
| … |
… |
|
| 296 | 296 | " description,author,ipnr) " |
| 297 | 297 | "VALUES ('ticket',%s,'file.txt',1234,%s," |
| 298 | 298 | " 'My file','mark','')", |
| 299 | | (str(tkt_id), to_timestamp(t2))) |
| | 299 | (str(tkt_id), to_utimestamp(t2))) |
| 300 | 300 | db.commit() |
| 301 | 301 | t3 = datetime(2001, 1, 1, 1, 1, 3, 0, utc) |
| 302 | 302 | ticket.save_changes('jim', 'Other', t3) |
| … |
… |
|
| 308 | 308 | sorted(log[1:3])) |
| 309 | 309 | self.assertEqual((t3, 'jim', 'comment', '2', 'Other', True), log[3]) |
| 310 | 310 | |
| | 311 | def test_subsecond_change(self): |
| | 312 | """Perform two ticket changes within a second.""" |
| | 313 | tkt_id = self._insert_ticket('Test', reporter='joe', component='foo') |
| | 314 | ticket = Ticket(self.env, tkt_id) |
| | 315 | t1 = datetime(2001, 1, 1, 1, 1, 1, 123456, utc) |
| | 316 | ticket.save_changes('jane', 'Testing', t1) |
| | 317 | t2 = datetime(2001, 1, 1, 1, 1, 1, 123789, utc) |
| | 318 | ticket.save_changes('jim', 'Other', t2) |
| | 319 | log = ticket.get_changelog() |
| | 320 | self.assertEqual(2, len(log)) |
| | 321 | self.assertEqual((t1, 'jane', 'comment', '1', 'Testing', True), log[0]) |
| | 322 | self.assertEqual((t2, 'jim', 'comment', '2', 'Other', True), log[1]) |
| | 323 | |
| 311 | 324 | def test_changelog_with_reverted_change(self): |
| 312 | 325 | tkt_id = self._insert_ticket('Test', reporter='joe', component='foo') |
| 313 | 326 | ticket = Ticket(self.env, tkt_id) |
| … |
… |
|
| 387 | 400 | |
| 388 | 401 | def _find_comment(self, ticket, cnum): |
| 389 | 402 | (ts, author, comment) = ticket._find_comment(cnum, self.db) |
| 390 | | return datetime.fromtimestamp(ts, utc) |
| | 403 | return from_utimestamp(ts) |
| 391 | 404 | |
| 392 | 405 | def assertChange(self, ticket, cnum, date, author, **fields): |
| 393 | 406 | change = ticket.get_change(cnum) |
| … |
… |
|
| 411 | 424 | self.assertChange(ticket, 1, self.t1, 'jack', |
| 412 | 425 | comment=dict(author='jack', old='1', new='New comment 1'), |
| 413 | 426 | _comment0=dict(author='joe', old='Comment 1', |
| 414 | | new=str(to_timestamp(t)))) |
| | 427 | new=str(to_utimestamp(t)))) |
| 415 | 428 | |
| 416 | 429 | def test_threading(self): |
| 417 | 430 | """Check modification of a "threaded" comment""" |
| … |
… |
|
| 423 | 436 | owner=dict(author='john', old='john', new='jack'), |
| 424 | 437 | comment=dict(author='john', old='1.2', new='New comment 2'), |
| 425 | 438 | _comment0=dict(author='joe', old='Comment 2', |
| 426 | | new=str(to_timestamp(t)))) |
| | 439 | new=str(to_utimestamp(t)))) |
| 427 | 440 | |
| 428 | 441 | def test_modify_missing_cnum(self): |
| 429 | 442 | """Editing a comment with no cnum in oldvalue""" |
| … |
… |
|
| 440 | 453 | keywords=dict(author='jim', old='a, b, c', new='a, b'), |
| 441 | 454 | comment=dict(author='jim', old='', new='New comment 3'), |
| 442 | 455 | _comment0=dict(author='joe', old='Comment 3', |
| 443 | | new=str(to_timestamp(t)))) |
| | 456 | new=str(to_utimestamp(t)))) |
| 444 | 457 | |
| 445 | 458 | def test_modify_missing_comment(self): |
| 446 | 459 | """Editing a comment where the comment field is missing""" |
| … |
… |
|
| 457 | 470 | owner=dict(author='john', old='john', new='jack'), |
| 458 | 471 | comment=dict(author='john', old='', new='New comment 2'), |
| 459 | 472 | _comment0=dict(author='joe', old='', |
| 460 | | new=str(to_timestamp(t)))) |
| | 473 | new=str(to_utimestamp(t)))) |
| 461 | 474 | |
| 462 | 475 | def test_modify_missing_cnums_and_comment(self): |
| 463 | 476 | """Editing a comments when all cnums are missing and one comment |
| … |
… |
|
| 481 | 494 | keywords=dict(author='jim', old='a, b, c', new='a, b'), |
| 482 | 495 | comment=dict(author='jim', old='', new='New comment 3'), |
| 483 | 496 | _comment0=dict(author='joe', old='Comment 3', |
| 484 | | new=str(to_timestamp(t)))) |
| | 497 | new=str(to_utimestamp(t)))) |
| 485 | 498 | |
| 486 | 499 | # Modify missing comment |
| 487 | 500 | t = self.created + timedelta(seconds=60) |
| … |
… |
|
| 491 | 504 | owner=dict(author='john', old='john', new='jack'), |
| 492 | 505 | comment=dict(author='john', old='', new='New comment 2'), |
| 493 | 506 | _comment0=dict(author='joe', old='', |
| 494 | | new=str(to_timestamp(t)))) |
| | 507 | new=str(to_utimestamp(t)))) |
| 495 | 508 | |
| 496 | 509 | def test_missing_comment_edit(self): |
| 497 | 510 | """Modify a comment where one edit is missing""" |
| … |
… |
|
| 506 | 519 | self.assertChange(ticket, 1, self.t1, 'jack', |
| 507 | 520 | comment=dict(author='jack', old='1', new='Other comment 1'), |
| 508 | 521 | _comment0=dict(author='joe', old='Comment 1', |
| 509 | | new=str(to_timestamp(t1))), |
| | 522 | new=str(to_utimestamp(t1))), |
| 510 | 523 | _comment1=dict(author='joe', old='New comment 1', |
| 511 | | new=str(to_timestamp(t2)))) |
| | 524 | new=str(to_utimestamp(t2)))) |
| 512 | 525 | |
| 513 | 526 | cursor = self.db.cursor() |
| 514 | 527 | cursor.execute("DELETE FROM ticket_change " |
| … |
… |
|
| 522 | 535 | self.assertChange(ticket, 1, self.t1, 'jack', |
| 523 | 536 | comment=dict(author='jack', old='1', new='Newest comment 1'), |
| 524 | 537 | _comment1=dict(author='joe', old='New comment 1', |
| 525 | | new=str(to_timestamp(t2))), |
| | 538 | new=str(to_utimestamp(t2))), |
| 526 | 539 | _comment2=dict(author='joe', old='Other comment 1', |
| 527 | | new=str(to_timestamp(t3)))) |
| | 540 | new=str(to_utimestamp(t3)))) |
| 528 | 541 | |
| 529 | 542 | def test_comment_history(self): |
| 530 | 543 | """Check the generation of the comment history""" |
| … |
… |
|
| 725 | 738 | |
| 726 | 739 | cursor = self.db.cursor() |
| 727 | 740 | cursor.execute("SELECT * FROM milestone WHERE name='Test'") |
| 728 | | self.assertEqual(('Test', to_timestamp(t1), to_timestamp(t2), 'Foo bar'), |
| | 741 | self.assertEqual(('Test', to_utimestamp(t1), to_utimestamp(t2), |
| | 742 | 'Foo bar'), |
| 729 | 743 | cursor.fetchone()) |
| 730 | 744 | |
| 731 | 745 | def test_update_milestone_without_name(self): |
-
diff --git a/trac/ticket/tests/query.py b/trac/ticket/tests/query.py
|
a
|
b
|
|
| 362 | 362 | LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority) |
| 363 | 363 | WHERE (((%(cast_time)s>=%%s AND %(cast_time)s<%%s))) |
| 364 | 364 | ORDER BY COALESCE(t.id,0)=0,t.id""" % { |
| 365 | | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int')}) |
| 366 | | self.assertEqual([1217548800, 1220227200], args) |
| | 365 | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int64')}) |
| | 366 | self.assertEqual([1217548800000000L, 1220227200000000L], args) |
| 367 | 367 | tickets = query.execute(self.req) |
| 368 | 368 | |
| 369 | 369 | def test_constrained_by_time_range_exclusion(self): |
| … |
… |
|
| 375 | 375 | LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority) |
| 376 | 376 | WHERE ((NOT (%(cast_time)s>=%%s AND %(cast_time)s<%%s))) |
| 377 | 377 | ORDER BY COALESCE(t.id,0)=0,t.id""" % { |
| 378 | | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int')}) |
| 379 | | self.assertEqual([1217548800, 1220227200], args) |
| | 378 | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int64')}) |
| | 379 | self.assertEqual([1217548800000000L, 1220227200000000L], args) |
| 380 | 380 | tickets = query.execute(self.req) |
| 381 | 381 | |
| 382 | 382 | def test_constrained_by_time_range_open_right(self): |
| … |
… |
|
| 388 | 388 | LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority) |
| 389 | 389 | WHERE ((%(cast_time)s>=%%s)) |
| 390 | 390 | ORDER BY COALESCE(t.id,0)=0,t.id""" % { |
| 391 | | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int')}) |
| 392 | | self.assertEqual([1217548800], args) |
| | 391 | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int64')}) |
| | 392 | self.assertEqual([1217548800000000L], args) |
| 393 | 393 | tickets = query.execute(self.req) |
| 394 | 394 | |
| 395 | 395 | def test_constrained_by_time_range_open_left(self): |
| … |
… |
|
| 401 | 401 | LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority) |
| 402 | 402 | WHERE ((%(cast_time)s<%%s)) |
| 403 | 403 | ORDER BY COALESCE(t.id,0)=0,t.id""" % { |
| 404 | | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int')}) |
| 405 | | self.assertEqual([1220227200], args) |
| | 404 | 'cast_time': self.env.get_db_cnx().cast('t.time', 'int64')}) |
| | 405 | self.assertEqual([1220227200000000L], args) |
| 406 | 406 | tickets = query.execute(self.req) |
| 407 | 407 | |
| 408 | 408 | def test_constrained_by_time_range_modified(self): |
| … |
… |
|
| 414 | 414 | LEFT OUTER JOIN enum AS priority ON (priority.type='priority' AND priority.name=priority) |
| 415 | 415 | WHERE (((%(cast_changetime)s>=%%s AND %(cast_changetime)s<%%s))) |
| 416 | 416 | ORDER BY COALESCE(t.id,0)=0,t.id""" % { |
| 417 | | 'cast_changetime': self.env.get_db_cnx().cast('t.changetime', 'int')}) |
| 418 | | self.assertEqual([1217548800, 1220227200], args) |
| | 417 | 'cast_changetime': self.env.get_db_cnx().cast('t.changetime', 'int64')}) |
| | 418 | self.assertEqual([1217548800000000L, 1220227200000000L], args) |
| 419 | 419 | tickets = query.execute(self.req) |
| 420 | 420 | |
| 421 | 421 | def test_constrained_by_keywords(self): |
-
diff --git a/trac/ticket/web_ui.py b/trac/ticket/web_ui.py
|
a
|
b
|
|
| 36 | 36 | from trac.timeline.api import ITimelineEventProvider |
| 37 | 37 | from trac.util import get_reporter_id |
| 38 | 38 | from trac.util.compat import any |
| 39 | | from trac.util.datefmt import format_datetime, to_timestamp, utc |
| | 39 | from trac.util.datefmt import format_datetime, from_utimestamp, \ |
| | 40 | to_utimestamp, utc |
| 40 | 41 | from trac.util.text import exception_to_unicode, obfuscate_email_address, \ |
| 41 | 42 | shorten_line, to_unicode |
| 42 | 43 | from trac.util.presentation import separated |
| … |
… |
|
| 214 | 215 | ': ', |
| 215 | 216 | ticketsystem.format_summary(summary, status, |
| 216 | 217 | resolution, type)), |
| 217 | | datetime.fromtimestamp(ts, utc), author, |
| | 218 | from_utimestamp(ts), author, |
| 218 | 219 | shorten_result(desc, terms)) |
| 219 | 220 | |
| 220 | 221 | # Attachments |
| … |
… |
|
| 231 | 232 | yield ('ticket_details', _('Ticket updates'), False) |
| 232 | 233 | |
| 233 | 234 | def get_timeline_events(self, req, start, stop, filters): |
| 234 | | ts_start = to_timestamp(start) |
| 235 | | ts_stop = to_timestamp(stop) |
| | 235 | ts_start = to_utimestamp(start) |
| | 236 | ts_stop = to_utimestamp(stop) |
| 236 | 237 | |
| 237 | 238 | status_map = {'new': ('newticket', N_('created')), |
| 238 | 239 | 'reopened': ('reopenedticket', N_('reopened')), |
| … |
… |
|
| 268 | 269 | else: |
| 269 | 270 | return None |
| 270 | 271 | kind, verb = status_map[status] |
| 271 | | return (kind, datetime.fromtimestamp(ts, utc), author, |
| | 272 | return (kind, from_utimestamp(ts), author, |
| 272 | 273 | (ticket, verb, info, summary, status, resolution, type, |
| 273 | 274 | description, comment, cid)) |
| 274 | 275 | |
| … |
… |
|
| 1646 | 1647 | rev = int(field[8:]) |
| 1647 | 1648 | comment_history.setdefault(rev, {}).update({'comment': old}) |
| 1648 | 1649 | comment_history.setdefault(rev + 1, {}).update( |
| 1649 | | {'author': author, |
| 1650 | | 'date': datetime.fromtimestamp(int(new), utc)}) |
| | 1650 | {'author': author, 'date': from_utimestamp(long(new))}) |
| 1651 | 1651 | elif old or new: |
| 1652 | 1652 | current['fields'][field] = {'old': old, 'new': new} |
| 1653 | 1653 | if current: |
-
diff --git a/trac/timeline/web_ui.py b/trac/timeline/web_ui.py
|
a
|
b
|
|
| 29 | 29 | from trac.perm import IPermissionRequestor |
| 30 | 30 | from trac.timeline.api import ITimelineEventProvider |
| 31 | 31 | from trac.util.datefmt import format_date, format_datetime, parse_date, \ |
| 32 | | to_timestamp, utc, pretty_timedelta |
| | 32 | to_utimestamp, utc, pretty_timedelta |
| 33 | 33 | from trac.util.text import exception_to_unicode, to_unicode |
| 34 | 34 | from trac.util.translation import _, tag_ |
| 35 | 35 | from trac.web import IRequestHandler, IRequestFilter |
| … |
… |
|
| 106 | 106 | precision = timedelta(hours=1) |
| 107 | 107 | else: |
| 108 | 108 | precision = None |
| 109 | | fromdate = fromdate.replace(hour=23, minute=59, second=59) |
| | 109 | fromdate = fromdate.replace(hour=23, minute=59, second=59, |
| | 110 | microsecond=999999) |
| 110 | 111 | try: |
| 111 | 112 | daysback = int(req.args.get('daysback', '')) |
| 112 | 113 | except ValueError: |
| … |
… |
|
| 312 | 313 | kind, date, author, data = event |
| 313 | 314 | render = lambda field, context: \ |
| 314 | 315 | provider.render_timeline_event(context, field, event) |
| 315 | | if isinstance(date, datetime): |
| 316 | | dateuid = to_timestamp(date) |
| 317 | | else: |
| 318 | | dateuid = date |
| | 316 | if not isinstance(date, datetime): |
| 319 | 317 | date = datetime.fromtimestamp(date, utc) |
| | 318 | dateuid = to_utimestamp(date) |
| 320 | 319 | return {'kind': kind, 'author': author, 'date': date, |
| 321 | 320 | 'dateuid': dateuid, 'render': render, 'event': event, |
| 322 | 321 | 'data': data, 'provider': provider} |
-
diff --git a/trac/upgrades/db15.py b/trac/upgrades/db15.py
|
a
|
b
|
|
| 10 | 10 | Column('authenticated', type='int'), |
| 11 | 11 | Column('var_name'), |
| 12 | 12 | Column('var_value')] |
| 13 | | db_backend, _ = DatabaseManager(env)._get_connector() |
| | 13 | db_backend, _ = DatabaseManager(env).get_connector() |
| 14 | 14 | for stmt in db_backend.to_sql(session_table): |
| 15 | 15 | cursor.execute(stmt) |
| 16 | 16 | |
-
diff --git a/trac/upgrades/db17.py b/trac/upgrades/db17.py
|
a
|
b
|
|
| 16 | 16 | Column('base_rev'), |
| 17 | 17 | Index(['rev']) |
| 18 | 18 | ] |
| 19 | | db_connector, _ = DatabaseManager(env)._get_connector() |
| | 19 | db_connector, _ = DatabaseManager(env).get_connector() |
| 20 | 20 | for stmt in db_connector.to_sql(table): |
| 21 | 21 | cursor.execute(stmt) |
| 22 | 22 | |
-
diff --git a/trac/upgrades/db18.py b/trac/upgrades/db18.py
|
a
|
b
|
|
| 29 | 29 | Index(['ticket']), |
| 30 | 30 | Index(['time'])]] |
| 31 | 31 | |
| 32 | | db_connector, _ = DatabaseManager(env)._get_connector() |
| | 32 | db_connector, _ = DatabaseManager(env).get_connector() |
| 33 | 33 | for table in tables: |
| 34 | 34 | for stmt in db_connector.to_sql(table): |
| 35 | 35 | cursor.execute(stmt) |
-
diff --git a/trac/upgrades/db19.py b/trac/upgrades/db19.py
|
a
|
b
|
|
| 13 | 13 | Column('query'), |
| 14 | 14 | Column('description') |
| 15 | 15 | ] |
| 16 | | db_connector, _ = DatabaseManager(env)._get_connector() |
| | 16 | db_connector, _ = DatabaseManager(env).get_connector() |
| 17 | 17 | for stmt in db_connector.to_sql(table): |
| 18 | 18 | cursor.execute(stmt) |
| 19 | 19 | |
-
diff --git a/trac/upgrades/db22.py b/trac/upgrades/db22.py
|
a
|
b
|
|
| 6 | 6 | Column('id'), |
| 7 | 7 | Column('generation', type='int') |
| 8 | 8 | ] |
| 9 | | db_connector, _ = DatabaseManager(env)._get_connector() |
| | 9 | db_connector, _ = DatabaseManager(env).get_connector() |
| 10 | 10 | for stmt in db_connector.to_sql(table): |
| 11 | 11 | cursor.execute(stmt) |
-
diff --git a/trac/upgrades/db25.py b/trac/upgrades/db25.py
new file mode 100644
|
-
|
+
|
|
| | 1 | from trac.db import DatabaseManager |
| | 2 | |
| | 3 | |
| | 4 | def do_upgrade(env, ver, cursor): |
| | 5 | """Convert time values from integer seconds to integer microseconds.""" |
| | 6 | tables = [ |
| | 7 | ('attachment', {'time': ('int', 'int64')}), |
| | 8 | ('wiki', {'time': ('int', 'int64')}), |
| | 9 | ('revision', {'time': ('int', 'int64')}), |
| | 10 | ('ticket', {'time': ('int', 'int64'), |
| | 11 | 'changetime': ('int', 'int64')}), |
| | 12 | ('ticket_change', {'time': ('int', 'int64')}), |
| | 13 | ('milestone', {'due': ('int', 'int64'), |
| | 14 | 'completed': ('int', 'int64')}), |
| | 15 | ('version', {'time': ('int', 'int64')}), |
| | 16 | ] |
| | 17 | |
| | 18 | db_connector, _ = DatabaseManager(env).get_connector() |
| | 19 | db = env.get_db_cnx() |
| | 20 | for table, columns in tables: |
| | 21 | # Alter column types |
| | 22 | for sql in db_connector.alter_column_types(table, columns): |
| | 23 | cursor.execute(sql) |
| | 24 | |
| | 25 | # Convert timestamps to microseconds |
| | 26 | cursor.execute("UPDATE %s SET %s" % (table, |
| | 27 | ', '.join("%s=%s*1000000" % (column, column) |
| | 28 | for column in columns))) |
| | 29 | |
| | 30 | # Convert comment edit timestamps to microseconds |
| | 31 | cursor.execute("UPDATE ticket_change SET newvalue=%s*1000000 " |
| | 32 | "WHERE field %s" % (db.cast('newvalue', 'int64'), |
| | 33 | db.like()), |
| | 34 | ('_comment%',)) |
-
diff --git a/trac/util/datefmt.py b/trac/util/datefmt.py
|
a
|
b
|
|
| 61 | 61 | else: |
| 62 | 62 | return 0 |
| 63 | 63 | |
| | 64 | def to_utimestamp(dt): |
| | 65 | """Return a microsecond POSIX timestamp for the given `datetime`.""" |
| | 66 | if not dt: |
| | 67 | return 0 |
| | 68 | diff = dt - _epoc |
| | 69 | return (diff.days * 86400000000L + diff.seconds * 1000000 |
| | 70 | + diff.microseconds) |
| | 71 | |
| | 72 | def from_utimestamp(ts): |
| | 73 | """Return the `datetime` for the given microsecond POSIX timestamp.""" |
| | 74 | return _epoc + timedelta(microseconds=ts or 0) |
| 64 | 75 | |
| 65 | 76 | # -- formatting |
| 66 | 77 | |
-
diff --git a/trac/util/tests/datefmt.py b/trac/util/tests/datefmt.py
|
a
|
b
|
|
| 113 | 113 | datefmt.format_date(a_date, format='%Y-%m-%d')) |
| 114 | 114 | |
| 115 | 115 | |
| | 116 | class UTimestampTestCase(unittest.TestCase): |
| | 117 | |
| | 118 | def test_sub_second(self): |
| | 119 | t = datetime.datetime(2001, 2, 3, 4, 5, 6, 123456, datefmt.utc) |
| | 120 | ts = datefmt.to_utimestamp(t) |
| | 121 | self.assertEqual(t, datefmt.from_utimestamp(ts)) |
| | 122 | |
| | 123 | |
| 116 | 124 | def suite(): |
| 117 | 125 | suite = unittest.TestSuite() |
| 118 | 126 | if PytzTestCase: |
| … |
… |
|
| 120 | 128 | else: |
| 121 | 129 | print "SKIP: utils/tests/datefmt.py (no pytz installed)" |
| 122 | 130 | suite.addTest(unittest.makeSuite(DateFormatTestCase)) |
| | 131 | suite.addTest(unittest.makeSuite(UTimestampTestCase)) |
| 123 | 132 | return suite |
| 124 | 133 | |
| 125 | 134 | if __name__ == '__main__': |
-
diff --git a/trac/versioncontrol/cache.py b/trac/versioncontrol/cache.py
|
a
|
b
|
|
| 14 | 14 | # |
| 15 | 15 | # Author: Christopher Lenz <cmlenz@gmx.de> |
| 16 | 16 | |
| 17 | | from datetime import datetime |
| 18 | 17 | import os |
| 19 | 18 | |
| 20 | 19 | from trac.cache import CacheProxy |
| 21 | 20 | from trac.core import TracError |
| 22 | 21 | from trac.db.util import with_transaction |
| 23 | | from trac.util.datefmt import utc, to_timestamp |
| | 22 | from trac.util.datefmt import from_utimestamp, to_utimestamp |
| 24 | 23 | from trac.util.translation import _ |
| 25 | 24 | from trac.versioncontrol import Changeset, Node, Repository, NoSuchChangeset |
| 26 | 25 | |
| … |
… |
|
| 75 | 74 | cursor.execute("SELECT rev FROM revision " |
| 76 | 75 | "WHERE repos=%s AND time >= %s AND time < %s " |
| 77 | 76 | "ORDER BY time DESC, rev DESC", |
| 78 | | (self.id, to_timestamp(start), |
| 79 | | to_timestamp(stop))) |
| | 77 | (self.id, to_utimestamp(start), to_utimestamp(stop))) |
| 80 | 78 | for rev, in cursor: |
| 81 | 79 | try: |
| 82 | 80 | yield self.get_changeset(rev) |
| … |
… |
|
| 95 | 93 | WHERE repos=%s AND rev=%s |
| 96 | 94 | """, (self.id, str(cset.rev))) |
| 97 | 95 | for time, author, message in cursor: |
| 98 | | date = datetime.fromtimestamp(time, utc) |
| 99 | 96 | old_cset[0] = Changeset(self.repos, cset.rev, message, author, |
| 100 | | date) |
| | 97 | from_utimestamp(time)) |
| 101 | 98 | cursor.execute(""" |
| 102 | 99 | UPDATE revision SET time=%s, author=%s, message=%s |
| 103 | 100 | WHERE repos=%s AND rev=%s |
| 104 | | """, (to_timestamp(cset.date), cset.author, cset.message, |
| | 101 | """, (to_utimestamp(cset.date), cset.author, cset.message, |
| 105 | 102 | self.id, str(cset.rev))) |
| 106 | 103 | return old_cset[0] |
| 107 | 104 | |
| … |
… |
|
| 237 | 234 | (repos,rev,time,author,message) |
| 238 | 235 | VALUES (%s,%s,%s,%s,%s) |
| 239 | 236 | """, (self.id, str(next_youngest), |
| 240 | | to_timestamp(cset.date), |
| | 237 | to_utimestamp(cset.date), |
| 241 | 238 | cset.author, cset.message)) |
| 242 | 239 | except Exception, e: # *another* 1.1. resync attempt won |
| 243 | 240 | self.log.warning('Revision %s already cached: %r', |
| … |
… |
|
| 403 | 400 | row = cursor.fetchone() |
| 404 | 401 | if row: |
| 405 | 402 | _date, author, message = row |
| 406 | | date = datetime.fromtimestamp(_date, utc) |
| | 403 | date = from_utimestamp(_date) |
| 407 | 404 | Changeset.__init__(self, repos, rev, message, author, date) |
| 408 | 405 | else: |
| 409 | 406 | raise NoSuchChangeset(rev) |
-
diff --git a/trac/versioncontrol/svn_fs.py b/trac/versioncontrol/svn_fs.py
|
a
|
b
|
|
| 46 | 46 | import os.path |
| 47 | 47 | import weakref |
| 48 | 48 | import posixpath |
| 49 | | from datetime import datetime |
| 50 | 49 | |
| 51 | 50 | from trac.config import ListOption |
| 52 | 51 | from trac.core import * |
| … |
… |
|
| 57 | 56 | from trac.util import embedded_numbers |
| 58 | 57 | from trac.util.text import exception_to_unicode, to_unicode |
| 59 | 58 | from trac.util.translation import _ |
| 60 | | from trac.util.datefmt import utc |
| | 59 | from trac.util.datefmt import from_utimestamp |
| 61 | 60 | |
| 62 | 61 | |
| 63 | 62 | application_pool = None |
| … |
… |
|
| 780 | 779 | core.SVN_PROP_REVISION_DATE, self.pool()) |
| 781 | 780 | if not _date: |
| 782 | 781 | return None |
| 783 | | ts = core.svn_time_from_cstring(_date, self.pool()) / 1000000 |
| 784 | | return datetime.fromtimestamp(ts, utc) |
| | 782 | return from_utimestamp(core.svn_time_from_cstring(_date, self.pool())) |
| 785 | 783 | |
| 786 | 784 | def _get_prop(self, name): |
| 787 | 785 | return fs.node_prop(self.root, self._scoped_path_utf8, name, |
| … |
… |
|
| 845 | 843 | author = author and to_unicode(author, 'utf-8') |
| 846 | 844 | _date = self._get_prop(core.SVN_PROP_REVISION_DATE) |
| 847 | 845 | if _date: |
| 848 | | ts = core.svn_time_from_cstring(_date, self.pool()) / 1000000 |
| 849 | | date = datetime.fromtimestamp(ts, utc) |
| | 846 | ts = core.svn_time_from_cstring(_date, self.pool()) |
| | 847 | date = from_utimestamp(ts) |
| 850 | 848 | else: |
| 851 | 849 | date = None |
| 852 | 850 | Changeset.__init__(self, repos, rev, message, author, date) |
-
diff --git a/trac/versioncontrol/tests/cache.py b/trac/versioncontrol/tests/cache.py
|
a
|
b
|
|
| 17 | 17 | from datetime import datetime |
| 18 | 18 | |
| 19 | 19 | from trac.test import EnvironmentStub, Mock |
| 20 | | from trac.util.datefmt import to_timestamp, utc |
| | 20 | from trac.util.datefmt import to_utimestamp, utc |
| 21 | 21 | from trac.versioncontrol import Repository, Changeset, Node, NoSuchChangeset |
| 22 | 22 | from trac.versioncontrol.cache import CachedRepository |
| 23 | 23 | |
| … |
… |
|
| 102 | 102 | |
| 103 | 103 | cursor = self.db.cursor() |
| 104 | 104 | cursor.execute("SELECT rev,time,author,message FROM revision") |
| 105 | | self.assertEquals(('0', to_timestamp(t1), '', ''), cursor.fetchone()) |
| 106 | | self.assertEquals(('1', to_timestamp(t2), 'joe', 'Import'), |
| | 105 | self.assertEquals(('0', to_utimestamp(t1), '', ''), cursor.fetchone()) |
| | 106 | self.assertEquals(('1', to_utimestamp(t2), 'joe', 'Import'), |
| 107 | 107 | cursor.fetchone()) |
| 108 | 108 | self.assertEquals(None, cursor.fetchone()) |
| 109 | 109 | cursor.execute(""" |
| … |
… |
|
| 121 | 121 | t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) |
| 122 | 122 | t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) |
| 123 | 123 | self.preset_cache( |
| 124 | | (('0', to_timestamp(t1), '', ''), []), |
| 125 | | (('1', to_timestamp(t2), 'joe', 'Import'), |
| | 124 | (('0', to_utimestamp(t1), '', ''), []), |
| | 125 | (('1', to_utimestamp(t2), 'joe', 'Import'), |
| 126 | 126 | [('trunk', 'D', 'A', None, None), |
| 127 | 127 | ('trunk/README', 'F', 'A', None, None)]), |
| 128 | 128 | ) |
| … |
… |
|
| 143 | 143 | cursor.execute(""" |
| 144 | 144 | SELECT time,author,message FROM revision WHERE rev='2' |
| 145 | 145 | """) |
| 146 | | self.assertEquals((to_timestamp(t3), 'joe', 'Update'), |
| | 146 | self.assertEquals((to_utimestamp(t3), 'joe', 'Update'), |
| 147 | 147 | cursor.fetchone()) |
| 148 | 148 | self.assertEquals(None, cursor.fetchone()) |
| 149 | 149 | cursor.execute(""" |
| … |
… |
|
| 159 | 159 | t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) |
| 160 | 160 | t3 = datetime(2003, 1, 1, 1, 1, 1, 0, utc) |
| 161 | 161 | self.preset_cache( |
| 162 | | (('0', to_timestamp(t1), '', ''), []), |
| 163 | | (('1', to_timestamp(t2), 'joe', 'Import'), |
| | 162 | (('0', to_utimestamp(t1), '', ''), []), |
| | 163 | (('1', to_utimestamp(t2), 'joe', 'Import'), |
| 164 | 164 | [('trunk', 'D', 'A', None, None), |
| 165 | 165 | ('trunk/README', 'F', 'A', None, None)]), |
| 166 | 166 | ) |
| … |
… |
|
| 183 | 183 | |
| 184 | 184 | cursor = self.db.cursor() |
| 185 | 185 | cursor.execute("SELECT time,author,message FROM revision") |
| 186 | | self.assertEquals((to_timestamp(t1), 'joe', '**empty**'), |
| | 186 | self.assertEquals((to_utimestamp(t1), 'joe', '**empty**'), |
| 187 | 187 | cursor.fetchone()) |
| 188 | | self.assertEquals((to_timestamp(t2), 'joe', 'Initial Import'), |
| | 188 | self.assertEquals((to_utimestamp(t2), 'joe', 'Initial Import'), |
| 189 | 189 | cursor.fetchone()) |
| 190 | | self.assertEquals((to_timestamp(t3), 'joe', 'Update'), |
| | 190 | self.assertEquals((to_utimestamp(t3), 'joe', 'Update'), |
| 191 | 191 | cursor.fetchone()) |
| 192 | 192 | self.assertEquals(None, cursor.fetchone()) |
| 193 | 193 | cursor.execute(""" |
| … |
… |
|
| 206 | 206 | t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) |
| 207 | 207 | t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) |
| 208 | 208 | self.preset_cache( |
| 209 | | (('0', to_timestamp(t1), '', ''), []), |
| 210 | | (('1', to_timestamp(t2), 'joe', 'Import'), |
| | 209 | (('0', to_utimestamp(t1), '', ''), []), |
| | 210 | (('1', to_utimestamp(t2), 'joe', 'Import'), |
| 211 | 211 | [('trunk', 'D', 'A', None, None), |
| 212 | 212 | ('trunk/README', 'F', 'A', None, None)]), |
| 213 | 213 | ) |
| … |
… |
|
| 226 | 226 | |
| 227 | 227 | cursor = self.db.cursor() |
| 228 | 228 | cursor.execute("SELECT time,author,message FROM revision ORDER BY rev") |
| 229 | | self.assertEquals((to_timestamp(t1), 'joe', '**empty**'), |
| | 229 | self.assertEquals((to_utimestamp(t1), 'joe', '**empty**'), |
| 230 | 230 | cursor.fetchone()) |
| 231 | | self.assertEquals((to_timestamp(t2), 'joe', 'Import'), |
| | 231 | self.assertEquals((to_utimestamp(t2), 'joe', 'Import'), |
| 232 | 232 | cursor.fetchone()) |
| 233 | 233 | self.assertEquals(None, cursor.fetchone()) |
| 234 | 234 | |
| … |
… |
|
| 236 | 236 | t1 = datetime(2001, 1, 1, 1, 1, 1, 0, utc) |
| 237 | 237 | t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) |
| 238 | 238 | self.preset_cache( |
| 239 | | (('0', to_timestamp(t1), '', ''), []), |
| 240 | | (('1', to_timestamp(t2), 'joe', 'Import'), |
| | 239 | (('0', to_utimestamp(t1), '', ''), []), |
| | 240 | (('1', to_utimestamp(t2), 'joe', 'Import'), |
| 241 | 241 | [('trunk', 'D', 'A', None, None), |
| 242 | 242 | ('trunk/RDME', 'F', 'A', None, None)]), |
| 243 | 243 | ) |
-
diff --git a/trac/versioncontrol/tests/svn_fs.py b/trac/versioncontrol/tests/svn_fs.py
|
a
|
b
|
|
| 146 | 146 | self.assertEqual(u'/tête', node.path) |
| 147 | 147 | self.assertEqual(Node.DIRECTORY, node.kind) |
| 148 | 148 | self.assertEqual(HEAD, node.rev) |
| 149 | | self.assertEqual(datetime(2007, 4, 30, 17, 45, 26, 0, utc), |
| | 149 | self.assertEqual(datetime(2007, 4, 30, 17, 45, 26, 234375, utc), |
| 150 | 150 | node.last_modified) |
| 151 | 151 | node = self.repos.get_node(u'/tête/README.txt') |
| 152 | 152 | self.assertEqual('README.txt', node.name) |
| 153 | 153 | self.assertEqual(u'/tête/README.txt', node.path) |
| 154 | 154 | self.assertEqual(Node.FILE, node.kind) |
| 155 | 155 | self.assertEqual(3, node.rev) |
| 156 | | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 0, utc), node.last_modified) |
| | 156 | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 234643, utc), node.last_modified) |
| 157 | 157 | |
| 158 | 158 | def test_get_node_specific_rev(self): |
| 159 | 159 | node = self.repos.get_node(u'/tête', 1) |
| … |
… |
|
| 161 | 161 | self.assertEqual(u'/tête', node.path) |
| 162 | 162 | self.assertEqual(Node.DIRECTORY, node.kind) |
| 163 | 163 | self.assertEqual(1, node.rev) |
| 164 | | self.assertEqual(datetime(2005, 4, 1, 10, 0, 52, 0, utc), node.last_modified) |
| | 164 | self.assertEqual(datetime(2005, 4, 1, 10, 0, 52, 353248, utc), node.last_modified) |
| 165 | 165 | node = self.repos.get_node(u'/tête/README.txt', 2) |
| 166 | 166 | self.assertEqual('README.txt', node.name) |
| 167 | 167 | self.assertEqual(u'/tête/README.txt', node.path) |
| 168 | 168 | self.assertEqual(Node.FILE, node.kind) |
| 169 | 169 | self.assertEqual(2, node.rev) |
| 170 | | self.assertEqual(datetime(2005, 4, 1, 13, 12, 18, 0, utc), node.last_modified) |
| | 170 | self.assertEqual(datetime(2005, 4, 1, 13, 12, 18, 216267, utc), node.last_modified) |
| 171 | 171 | |
| 172 | 172 | def test_get_dir_entries(self): |
| 173 | 173 | node = self.repos.get_node(u'/tête') |
| … |
… |
|
| 384 | 384 | self.assertEqual(0, chgset.rev) |
| 385 | 385 | self.assertEqual('', chgset.message) |
| 386 | 386 | self.assertEqual('', chgset.author) |
| 387 | | self.assertEqual(datetime(2005, 4, 1, 9, 57, 41, 0, utc), chgset.date) |
| | 387 | self.assertEqual(datetime(2005, 4, 1, 9, 57, 41, 312767, utc), chgset.date) |
| 388 | 388 | self.assertRaises(StopIteration, chgset.get_changes().next) |
| 389 | 389 | |
| 390 | 390 | def test_changeset_added_dirs(self): |
| … |
… |
|
| 392 | 392 | self.assertEqual(1, chgset.rev) |
| 393 | 393 | self.assertEqual('Initial directory layout.', chgset.message) |
| 394 | 394 | self.assertEqual('john', chgset.author) |
| 395 | | self.assertEqual(datetime(2005, 4, 1, 10, 0, 52, 0, utc), chgset.date) |
| | 395 | self.assertEqual(datetime(2005, 4, 1, 10, 0, 52, 353248, utc), chgset.date) |
| 396 | 396 | |
| 397 | 397 | changes = chgset.get_changes() |
| 398 | 398 | self.assertEqual(('branches', Node.DIRECTORY, Changeset.ADD, None, -1), |
| … |
… |
|
| 408 | 408 | self.assertEqual(3, chgset.rev) |
| 409 | 409 | self.assertEqual('Fixed README.\n', chgset.message) |
| 410 | 410 | self.assertEqual('kate', chgset.author) |
| 411 | | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 0, utc), chgset.date) |
| | 411 | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 234643, utc), chgset.date) |
| 412 | 412 | |
| 413 | 413 | changes = chgset.get_changes() |
| 414 | 414 | self.assertEqual((u'tête/README.txt', Node.FILE, Changeset.EDIT, |
| … |
… |
|
| 420 | 420 | self.assertEqual(5, chgset.rev) |
| 421 | 421 | self.assertEqual('Moved directories.', chgset.message) |
| 422 | 422 | self.assertEqual('kate', chgset.author) |
| 423 | | self.assertEqual(datetime(2005, 4, 1, 16, 25, 39, 0, utc), chgset.date) |
| | 423 | self.assertEqual(datetime(2005, 4, 1, 16, 25, 39, 658099, utc), chgset.date) |
| 424 | 424 | |
| 425 | 425 | changes = chgset.get_changes() |
| 426 | 426 | self.assertEqual((u'tête/dir1/dir2', Node.DIRECTORY, Changeset.MOVE, |
| … |
… |
|
| 434 | 434 | self.assertEqual(6, chgset.rev) |
| 435 | 435 | self.assertEqual('More things to read', chgset.message) |
| 436 | 436 | self.assertEqual('john', chgset.author) |
| 437 | | self.assertEqual(datetime(2005, 4, 1, 18, 56, 46, 0, utc), chgset.date) |
| | 437 | self.assertEqual(datetime(2005, 4, 1, 18, 56, 46, 985846, utc), chgset.date) |
| 438 | 438 | |
| 439 | 439 | changes = chgset.get_changes() |
| 440 | 440 | self.assertEqual((u'tête/README2.txt', Node.FILE, Changeset.COPY, |
| … |
… |
|
| 552 | 552 | self.assertEqual('/dir1', node.path) |
| 553 | 553 | self.assertEqual(Node.DIRECTORY, node.kind) |
| 554 | 554 | self.assertEqual(5, node.rev) |
| 555 | | self.assertEqual(datetime(2005, 4, 1, 16, 25, 39, 0, utc), node.last_modified) |
| | 555 | self.assertEqual(datetime(2005, 4, 1, 16, 25, 39, 658099, utc), node.last_modified) |
| 556 | 556 | node = self.repos.get_node('/README.txt') |
| 557 | 557 | self.assertEqual('README.txt', node.name) |
| 558 | 558 | self.assertEqual('/README.txt', node.path) |
| 559 | 559 | self.assertEqual(Node.FILE, node.kind) |
| 560 | 560 | self.assertEqual(3, node.rev) |
| 561 | | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 0, utc), node.last_modified) |
| | 561 | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 234643, utc), node.last_modified) |
| 562 | 562 | |
| 563 | 563 | def test_get_node_specific_rev(self): |
| 564 | 564 | node = self.repos.get_node('/dir1', 4) |
| … |
… |
|
| 566 | 566 | self.assertEqual('/dir1', node.path) |
| 567 | 567 | self.assertEqual(Node.DIRECTORY, node.kind) |
| 568 | 568 | self.assertEqual(4, node.rev) |
| 569 | | self.assertEqual(datetime(2005, 4, 1, 15, 42, 35, 0, utc), node.last_modified) |
| | 569 | self.assertEqual(datetime(2005, 4, 1, 15, 42, 35, 450595, utc), node.last_modified) |
| 570 | 570 | node = self.repos.get_node('/README.txt', 2) |
| 571 | 571 | self.assertEqual('README.txt', node.name) |
| 572 | 572 | self.assertEqual('/README.txt', node.path) |
| 573 | 573 | self.assertEqual(Node.FILE, node.kind) |
| 574 | 574 | self.assertEqual(2, node.rev) |
| 575 | | self.assertEqual(datetime(2005, 4, 1, 13, 12, 18, 0, utc), node.last_modified) |
| | 575 | self.assertEqual(datetime(2005, 4, 1, 13, 12, 18, 216267, utc), node.last_modified) |
| 576 | 576 | |
| 577 | 577 | def test_get_dir_entries(self): |
| 578 | 578 | node = self.repos.get_node('/') |
| … |
… |
|
| 677 | 677 | self.assertEqual(0, chgset.rev) |
| 678 | 678 | self.assertEqual('', chgset.message) |
| 679 | 679 | self.assertEqual('', chgset.author) |
| 680 | | self.assertEqual(datetime(2005, 4, 1, 9, 57, 41, 0, utc), chgset.date) |
| | 680 | self.assertEqual(datetime(2005, 4, 1, 9, 57, 41, 312767, utc), chgset.date) |
| 681 | 681 | self.assertRaises(StopIteration, chgset.get_changes().next) |
| 682 | 682 | |
| 683 | 683 | def test_changeset_added_dirs(self): |
| … |
… |
|
| 685 | 685 | self.assertEqual(4, chgset.rev) |
| 686 | 686 | self.assertEqual('More directories.', chgset.message) |
| 687 | 687 | self.assertEqual('john', chgset.author) |
| 688 | | self.assertEqual(datetime(2005, 4, 1, 15, 42, 35, 0, utc), chgset.date) |
| | 688 | self.assertEqual(datetime(2005, 4, 1, 15, 42, 35, 450595, utc), chgset.date) |
| 689 | 689 | |
| 690 | 690 | changes = chgset.get_changes() |
| 691 | 691 | self.assertEqual(('dir1', Node.DIRECTORY, 'add', None, -1), |
| … |
… |
|
| 701 | 701 | self.assertEqual(3, chgset.rev) |
| 702 | 702 | self.assertEqual('Fixed README.\n', chgset.message) |
| 703 | 703 | self.assertEqual('kate', chgset.author) |
| 704 | | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 0, utc), chgset.date) |
| | 704 | self.assertEqual(datetime(2005, 4, 1, 13, 24, 58, 234643, utc), chgset.date) |
| 705 | 705 | |
| 706 | 706 | changes = chgset.get_changes() |
| 707 | 707 | self.assertEqual(('README.txt', Node.FILE, Changeset.EDIT, |
| … |
… |
|
| 713 | 713 | self.assertEqual(5, chgset.rev) |
| 714 | 714 | self.assertEqual('Moved directories.', chgset.message) |
| 715 | 715 | self.assertEqual('kate', chgset.author) |
| 716 | | self.assertEqual(datetime(2005, 4, 1, 16, 25, 39, 0, utc), chgset.date) |
| | 716 | self.assertEqual(datetime(2005, 4, 1, 16, 25, 39, 658099, utc), chgset.date) |
| 717 | 717 | |
| 718 | 718 | changes = chgset.get_changes() |
| 719 | 719 | self.assertEqual(('dir1/dir2', Node.DIRECTORY, Changeset.MOVE, |
| … |
… |
|
| 727 | 727 | self.assertEqual(6, chgset.rev) |
| 728 | 728 | self.assertEqual('More things to read', chgset.message) |
| 729 | 729 | self.assertEqual('john', chgset.author) |
| 730 | | self.assertEqual(datetime(2005, 4, 1, 18, 56, 46, 0, utc), chgset.date) |
| | 730 | self.assertEqual(datetime(2005, 4, 1, 18, 56, 46, 985846, utc), chgset.date) |
| 731 | 731 | |
| 732 | 732 | changes = chgset.get_changes() |
| 733 | 733 | self.assertEqual(('README2.txt', Node.FILE, Changeset.COPY, |
-
diff --git a/trac/versioncontrol/web_ui/changeset.py b/trac/versioncontrol/web_ui/changeset.py
|
a
|
b
|
|
| 18 | 18 | # Christopher Lenz <cmlenz@gmx.de> |
| 19 | 19 | # Christian Boos <cboos@neuf.fr> |
| 20 | 20 | |
| 21 | | from datetime import datetime |
| 22 | 21 | from itertools import groupby |
| 23 | 22 | import os |
| 24 | 23 | import posixpath |
| … |
… |
|
| 35 | 34 | from trac.search import ISearchSource, search_to_sql, shorten_result |
| 36 | 35 | from trac.timeline.api import ITimelineEventProvider |
| 37 | 36 | from trac.util import content_disposition, embedded_numbers, pathjoin |
| 38 | | from trac.util.compat import any, set |
| 39 | | from trac.util.datefmt import pretty_timedelta, utc |
| | 37 | from trac.util.compat import any |
| | 38 | from trac.util.datefmt import from_utimestamp, pretty_timedelta |
| 40 | 39 | from trac.util.text import exception_to_unicode, to_unicode, \ |
| 41 | 40 | unicode_urlencode, shorten_line, CRLF |
| 42 | 41 | from trac.util.translation import _, ngettext |
| … |
… |
|
| 1113 | 1112 | if 'CHANGESET_VIEW' in req.perm(cset): |
| 1114 | 1113 | yield (req.href.changeset(rev, repos.reponame or None), |
| 1115 | 1114 | '[%s]: %s' % (rev, shorten_line(log)), |
| 1116 | | datetime.fromtimestamp(ts, utc), author, |
| 1117 | | shorten_result(log, terms)) |
| | 1115 | from_utimestamp(ts), author, shorten_result(log, terms)) |
| 1118 | 1116 | |
| 1119 | 1117 | |
| 1120 | 1118 | class AnyDiffModule(Component): |
-
diff --git a/trac/web/chrome.py b/trac/web/chrome.py
|
a
|
b
|
|
| 65 | 65 | shorten_line, unicode_quote_plus, to_unicode, \ |
| 66 | 66 | javascript_quote, exception_to_unicode |
| 67 | 67 | from trac.util.datefmt import pretty_timedelta, format_datetime, format_date, \ |
| 68 | | format_time, http_date, utc |
| | 68 | format_time, from_utimestamp, http_date, utc |
| 69 | 69 | from trac.util.translation import _ |
| 70 | 70 | from trac.web.api import IRequestHandler, ITemplateStreamFilter, HTTPNotFound |
| 71 | 71 | from trac.web.href import Href |
| … |
… |
|
| 754 | 754 | 'format_time': partial(format_time, tzinfo=tzinfo), |
| 755 | 755 | 'fromtimestamp': partial(datetime.datetime.fromtimestamp, |
| 756 | 756 | tz=tzinfo), |
| | 757 | 'from_utimestamp': from_utimestamp, |
| 757 | 758 | |
| 758 | 759 | # Wiki-formatting functions |
| 759 | 760 | 'wiki_to': partial(format_to, self.env), |
-
diff --git a/trac/web/tests/session.py b/trac/web/tests/session.py
|
a
|
b
|
|
| 166 | 166 | (0,)) |
| 167 | 167 | cursor.execute("INSERT INTO session " |
| 168 | 168 | "VALUES ('987654', 0, %s)", |
| 169 | | (time.time() - PURGE_AGE - 3600,)) |
| | 169 | (int(time.time() - PURGE_AGE - 3600),)) |
| 170 | 170 | cursor.execute("INSERT INTO session_attribute VALUES " |
| 171 | 171 | "('987654', 0, 'foo', 'bar')") |
| 172 | 172 | |
-
diff --git a/trac/wiki/admin.py b/trac/wiki/admin.py
|
a
|
b
|
|
| 15 | 15 | import os.path |
| 16 | 16 | import pkg_resources |
| 17 | 17 | import sys |
| 18 | | import time |
| 19 | 18 | |
| 20 | 19 | from trac.admin import * |
| 21 | 20 | from trac.core import * |
| … |
… |
|
| 24 | 23 | from trac.wiki.api import WikiSystem |
| 25 | 24 | from trac.util import read_file |
| 26 | 25 | from trac.util.compat import any |
| 27 | | from trac.util.datefmt import format_datetime, utc |
| | 26 | from trac.util.datefmt import format_datetime, from_utimestamp, \ |
| | 27 | to_utimestamp, utc |
| 28 | 28 | from trac.util.text import to_unicode, unicode_quote, unicode_unquote, \ |
| 29 | 29 | print_table, printout |
| 30 | 30 | from trac.util.translation import _ |
| … |
… |
|
| 147 | 147 | "SELECT 1+COALESCE(max(version),0),%s,%s," |
| 148 | 148 | " 'trac','127.0.0.1',%s FROM wiki " |
| 149 | 149 | "WHERE name=%s", |
| 150 | | (title, int(time.time()), data, title)) |
| | 150 | (title, to_utimestamp(datetime.now(utc)), data, |
| | 151 | title)) |
| 151 | 152 | if not old: |
| 152 | 153 | WikiSystem(self.env).pages.invalidate(db) |
| 153 | 154 | return result[0] |
| … |
… |
|
| 191 | 192 | cursor.execute("SELECT name, max(version), max(time) " |
| 192 | 193 | "FROM wiki GROUP BY name ORDER BY name") |
| 193 | 194 | print_table([(r[0], int(r[1]), |
| 194 | | format_datetime(datetime.fromtimestamp(r[2], utc), |
| | 195 | format_datetime(from_utimestamp(r[2]), |
| 195 | 196 | console_datetime_format)) |
| 196 | 197 | for r in cursor], |
| 197 | 198 | [_('Title'), _('Edits'), _('Modified')]) |
-
diff --git a/trac/wiki/macros.py b/trac/wiki/macros.py
|
a
|
b
|
|
| 14 | 14 | # |
| 15 | 15 | # Author: Christopher Lenz <cmlenz@gmx.de> |
| 16 | 16 | |
| 17 | | from datetime import datetime |
| 18 | 17 | from itertools import groupby |
| 19 | 18 | import inspect |
| 20 | 19 | import os |
| … |
… |
|
| 27 | 26 | from trac.core import * |
| 28 | 27 | from trac.resource import Resource, get_resource_url, get_resource_summary |
| 29 | 28 | from trac.util.compat import rpartition |
| 30 | | from trac.util.datefmt import format_date, utc |
| | 29 | from trac.util.datefmt import format_date, from_utimestamp |
| 31 | 30 | from trac.util.html import escape |
| 32 | 31 | from trac.util.presentation import separated |
| 33 | 32 | from trac.util.text import unquote, to_unicode |
| … |
… |
|
| 225 | 224 | for name, version, ts in cursor: |
| 226 | 225 | if not 'WIKI_VIEW' in formatter.perm('wiki', name, version): |
| 227 | 226 | continue |
| 228 | | time = datetime.fromtimestamp(ts, utc) |
| 229 | | date = format_date(time) |
| | 227 | date = format_date(from_utimestamp(ts)) |
| 230 | 228 | if date != prevdate: |
| 231 | 229 | prevdate = date |
| 232 | 230 | entries_per_date.append((date, [])) |
-
diff --git a/trac/wiki/model.py b/trac/wiki/model.py
|
a
|
b
|
|
| 21 | 21 | from trac.core import * |
| 22 | 22 | from trac.db.util import with_transaction |
| 23 | 23 | from trac.resource import Resource |
| 24 | | from trac.util.datefmt import utc, to_timestamp |
| | 24 | from trac.util.datefmt import from_utimestamp, to_utimestamp, utc |
| 25 | 25 | from trac.util.translation import _ |
| 26 | 26 | from trac.wiki.api import WikiSystem |
| 27 | 27 | |
| … |
… |
|
| 70 | 70 | version, time, author, text, comment, readonly = row |
| 71 | 71 | self.version = int(version) |
| 72 | 72 | self.author = author |
| 73 | | self.time = datetime.fromtimestamp(time, utc) |
| | 73 | self.time = from_utimestamp(time) |
| 74 | 74 | self.text = text |
| 75 | 75 | self.comment = comment |
| 76 | 76 | self.readonly = readonly and int(readonly) or 0 |
| … |
… |
|
| 132 | 132 | INSERT INTO wiki (name,version,time,author,ipnr,text, |
| 133 | 133 | comment,readonly) |
| 134 | 134 | VALUES (%s,%s,%s,%s,%s,%s,%s,%s) |
| 135 | | """, (self.name, self.version + 1, to_timestamp(t), |
| | 135 | """, (self.name, self.version + 1, to_utimestamp(t), |
| 136 | 136 | author, remote_addr, self.text, comment, |
| 137 | 137 | self.readonly)) |
| 138 | 138 | self.version += 1 |
| … |
… |
|
| 166 | 166 | "WHERE name=%s AND version<=%s " |
| 167 | 167 | "ORDER BY version DESC", (self.name, self.version)) |
| 168 | 168 | for version, ts, author, comment, ipnr in cursor: |
| 169 | | time = datetime.fromtimestamp(ts, utc) |
| 170 | | yield version, time, author, comment, ipnr |
| | 169 | yield version, from_utimestamp(ts), author, comment, ipnr |
-
diff --git a/trac/wiki/tests/model.py b/trac/wiki/tests/model.py
|
a
|
b
|
|
| 3 | 3 | |
| 4 | 4 | from trac.core import * |
| 5 | 5 | from trac.test import EnvironmentStub |
| 6 | | from trac.util.datefmt import utc, to_timestamp |
| | 6 | from trac.util.datefmt import utc, to_utimestamp |
| 7 | 7 | from trac.wiki import WikiPage, IWikiChangeListener |
| 8 | 8 | |
| 9 | 9 | |
| … |
… |
|
| 52 | 52 | t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) |
| 53 | 53 | cursor = self.db.cursor() |
| 54 | 54 | cursor.execute("INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", |
| 55 | | ('TestPage', 1, to_timestamp(t), 'joe', '::1', |
| | 55 | ('TestPage', 1, to_utimestamp(t), 'joe', '::1', |
| 56 | 56 | 'Bla bla', 'Testing', 0)) |
| 57 | 57 | |
| 58 | 58 | page = WikiPage(self.env, 'TestPage') |
| … |
… |
|
| 91 | 91 | cursor = self.db.cursor() |
| 92 | 92 | cursor.execute("SELECT version,time,author,ipnr,text,comment," |
| 93 | 93 | "readonly FROM wiki WHERE name=%s", ('TestPage',)) |
| 94 | | self.assertEqual((1, to_timestamp(t), 'joe', '::1', 'Bla bla', |
| | 94 | self.assertEqual((1, to_utimestamp(t), 'joe', '::1', 'Bla bla', |
| 95 | 95 | 'Testing', 0), |
| 96 | 96 | cursor.fetchone()) |
| 97 | 97 | |
| … |
… |
|
| 103 | 103 | t = datetime(2001, 1, 1, 1, 1, 1, 0, utc) |
| 104 | 104 | t2 = datetime(2002, 1, 1, 1, 1, 1, 0, utc) |
| 105 | 105 | cursor.execute("INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", |
| 106 | | ('TestPage', 1, to_timestamp(t), 'joe', '::1', |
| | 106 | ('TestPage', 1, to_utimestamp(t), 'joe', '::1', |
| 107 | 107 | 'Bla bla', 'Testing', 0)) |
| 108 | 108 | |
| 109 | 109 | page = WikiPage(self.env, 'TestPage') |
| … |
… |
|
| 119 | 119 | |
| 120 | 120 | cursor.execute("SELECT version,time,author,ipnr,text,comment," |
| 121 | 121 | "readonly FROM wiki WHERE name=%s", ('TestPage',)) |
| 122 | | self.assertEqual((1, to_timestamp(t), 'joe', '::1', 'Bla bla', |
| | 122 | self.assertEqual((1, to_utimestamp(t), 'joe', '::1', 'Bla bla', |
| 123 | 123 | 'Testing', 0), |
| 124 | 124 | cursor.fetchone()) |
| 125 | | self.assertEqual((2, to_timestamp(t2), 'kate', '192.168.0.101', 'Bla', |
| | 125 | self.assertEqual((2, to_utimestamp(t2), 'kate', '192.168.0.101', 'Bla', |
| 126 | 126 | 'Changing', 0), cursor.fetchone()) |
| 127 | 127 | |
| 128 | 128 | listener = TestWikiChangeListener(self.env) |
-
diff --git a/trac/wiki/web_ui.py b/trac/wiki/web_ui.py
|
a
|
b
|
|
| 16 | 16 | # Author: Jonas Borgström <jonas@edgewall.com> |
| 17 | 17 | # Christopher Lenz <cmlenz@gmx.de> |
| 18 | 18 | |
| 19 | | from datetime import datetime |
| 20 | 19 | import pkg_resources |
| 21 | 20 | import re |
| 22 | 21 | |
| … |
… |
|
| 32 | 31 | from trac.search import ISearchSource, search_to_sql, shorten_result |
| 33 | 32 | from trac.timeline.api import ITimelineEventProvider |
| 34 | 33 | from trac.util import get_reporter_id |
| 35 | | from trac.util.datefmt import to_timestamp, utc |
| | 34 | from trac.util.datefmt import from_utimestamp, to_utimestamp |
| 36 | 35 | from trac.util.text import shorten_line |
| 37 | 36 | from trac.util.translation import _ |
| 38 | 37 | from trac.versioncontrol.diff import get_diff_options, diff_blocks |
| … |
… |
|
| 626 | 625 | cursor = db.cursor() |
| 627 | 626 | cursor.execute("SELECT time,name,comment,author,version " |
| 628 | 627 | "FROM wiki WHERE time>=%s AND time<=%s", |
| 629 | | (to_timestamp(start), to_timestamp(stop))) |
| | 628 | (to_utimestamp(start), to_utimestamp(stop))) |
| 630 | 629 | for ts, name, comment, author, version in cursor: |
| 631 | 630 | wiki_page = wiki_realm(id=name, version=version) |
| 632 | 631 | if 'WIKI_VIEW' not in req.perm(wiki_page): |
| 633 | 632 | continue |
| 634 | | yield ('wiki', datetime.fromtimestamp(ts, utc), author, |
| | 633 | yield ('wiki', from_utimestamp(ts), author, |
| 635 | 634 | (wiki_page, comment)) |
| 636 | 635 | |
| 637 | 636 | # Attachments |
| … |
… |
|
| 682 | 681 | if 'WIKI_VIEW' in req.perm(page): |
| 683 | 682 | yield (get_resource_url(self.env, page, req.href), |
| 684 | 683 | '%s: %s' % (name, shorten_line(text)), |
| 685 | | datetime.fromtimestamp(ts, utc), author, |
| | 684 | from_utimestamp(ts), author, |
| 686 | 685 | shorten_result(text, terms)) |
| 687 | 686 | |
| 688 | 687 | # Attachments |