OSDN Git Service

- Partial fix for timezone handling.
authorcs <cs@e1914e07-63f8-0310-9059-d6d858d7cdca>
Sat, 25 May 2002 17:00:04 +0000 (17:00 +0000)
committercs <cs@e1914e07-63f8-0310-9059-d6d858d7cdca>
Sat, 25 May 2002 17:00:04 +0000 (17:00 +0000)
- export reports now support resource usage export
- documentation updated
- extended test suite to contain arbitrary tests in Misc directory
- changed version number to 1.1 (getting ready for next release)

git-svn-id: https://www.taskjuggler.org/svn/taskjuggler/trunk@140 e1914e07-63f8-0310-9059-d6d858d7cdca

23 files changed:
Makefile.am
TestSuite/.cvsignore [new file with mode: 0644]
TestSuite/Makefile.am [new file with mode: 0644]
TestSuite/Misc/export/project_a.tjp [new file with mode: 0644]
TestSuite/Misc/export/project_b1.tjp [new file with mode: 0644]
TestSuite/Misc/export/project_b2.tjp [new file with mode: 0644]
TestSuite/Misc/export/runtest [new file with mode: 0755]
TestSuite/Misc/export/workforce.tjsp [new file with mode: 0644]
TestSuite/Misc/testdir [new file with mode: 0755]
TestSuite/runtests
docs/en/index.sgml.in
taskjuggler/CoreAttributes.cpp
taskjuggler/ExportReport.cpp
taskjuggler/ExportReport.h
taskjuggler/ProjectFile.cpp
taskjuggler/ProjectFile.h
taskjuggler/ReportHtml.cpp
taskjuggler/ResourceList.cpp
taskjuggler/ResourceList.h
taskjuggler/Task.cpp
taskjuggler/Task.h
taskjuggler/Utility.cpp
taskjuggler/Utility.h

index 41066b3..492cc4a 100644 (file)
@@ -1,6 +1,6 @@
 ####### kdevelop will overwrite this part!!! (begin)##########
 
-SUBDIRS = taskjuggler docs
+SUBDIRS = taskjuggler docs TestSuite
 
 EXTRA_DIST = taskjuggler.kdevprj admin AUTHORS COPYING ChangeLog INSTALL README TODO taskjuggler.lsm 
 
diff --git a/TestSuite/.cvsignore b/TestSuite/.cvsignore
new file mode 100644 (file)
index 0000000..d436efd
--- /dev/null
@@ -0,0 +1,3 @@
+Makefile.in
+Makefile
+
diff --git a/TestSuite/Makefile.am b/TestSuite/Makefile.am
new file mode 100644 (file)
index 0000000..9698266
--- /dev/null
@@ -0,0 +1,3 @@
+all:
+       @ ./runtests
+
diff --git a/TestSuite/Misc/export/project_a.tjp b/TestSuite/Misc/export/project_a.tjp
new file mode 100644 (file)
index 0000000..5d60f2e
--- /dev/null
@@ -0,0 +1,45 @@
+project test "Test" "$Id" 2000-01-01 2000-03-10
+
+include "workforce.tjsp"
+
+task t1 "Task1" {
+       start 2000-01-01
+       effort 10d
+       allocate r1
+}
+
+task t2 "Task2" {
+       depends t1
+       effort 10d
+       allocate r2
+}
+
+task t3 "Task3" {
+       start 2000-01-01
+       length 13d
+       allocate r3
+}
+
+task t4 "Task4" {
+       depends t3
+       duration 26d
+}
+
+task t5 "Task5" {
+       depends t1
+       priority 1000
+       length 5d
+       allocate r2
+}
+
+htmltaskreport "project_a.html" {
+       columns name, start, end, daily
+       sorttasks nameup
+       hideresource 0
+}
+
+htmlresourcereport "project_a_res.html" {
+       columns name, daily
+  hidetask 0
+       sorttasks nameup
+}
diff --git a/TestSuite/Misc/export/project_b1.tjp b/TestSuite/Misc/export/project_b1.tjp
new file mode 100644 (file)
index 0000000..7dc17c4
--- /dev/null
@@ -0,0 +1,27 @@
+project test "Test" "$Id" 2000-01-01 2000-02-31
+
+include "workforce.tjsp"
+
+task t1 "Task1" {
+       start 2000-01-01
+       effort 10d
+       allocate r1
+}
+
+task t3 "Task3" {
+       start 2000-01-01
+       length 13d
+       allocate r3
+}
+
+task t5 "Task5" {
+       depends t1
+       priority 1000
+       length 5d
+       allocate r2
+}
+
+export "project_b1_export.tjsp" {
+  hideresource 0
+}
+
diff --git a/TestSuite/Misc/export/project_b2.tjp b/TestSuite/Misc/export/project_b2.tjp
new file mode 100644 (file)
index 0000000..a3a629e
--- /dev/null
@@ -0,0 +1,30 @@
+project test "Test" "$Id" 2000-01-01 2000-03-10
+
+include "workforce.tjsp"
+
+include "project_b1_export.tjsp"
+
+
+task t2 "Task2" {
+       depends t1
+       effort 10d
+       allocate r2
+}
+
+task t4 "Task4" {
+       depends t3
+       duration 26d
+}
+
+htmltaskreport "project_b.html" {
+       columns name, start, end, daily
+       sorttasks nameup
+       hideresource 0
+}
+
+htmlresourcereport "project_b_res.html" {
+       columns name, daily
+       hidetask 0
+       sorttasks nameup
+}
+
diff --git a/TestSuite/Misc/export/runtest b/TestSuite/Misc/export/runtest
new file mode 100755 (executable)
index 0000000..3242972
--- /dev/null
@@ -0,0 +1,16 @@
+../../../taskjuggler/taskjuggler project_a.tjp
+../../../taskjuggler/taskjuggler project_b1.tjp
+../../../taskjuggler/taskjuggler project_b2.tjp
+
+cmp project_a.html project_b.html
+if [ $? -ne 0 ] ; then
+  exit 1
+fi
+
+cmp project_a_res.html project_b_res.html
+if [ $? -ne 0 ] ; then
+  exit 1
+fi
+
+rm *.html project_b1_export.tjsp
+
diff --git a/TestSuite/Misc/export/workforce.tjsp b/TestSuite/Misc/export/workforce.tjsp
new file mode 100644 (file)
index 0000000..04e4342
--- /dev/null
@@ -0,0 +1,24 @@
+vacation "Monday off" 2000-01-03
+vacation "Wednesday off" 2000-01-05
+vacation "Few days off" 2000-01-12 - 2000-01-15
+
+resource team "Team" {
+       vacation 2000-01-21 - 2000-01-25
+       
+       resource r1 "Resource1" {
+               vacation 2000-01-17
+       }
+
+       resource r2 "Resource2" {
+               efficiency 0.5
+       }
+}
+
+supplement resource r2 {
+       vacation 2000-01-18
+}
+
+resource r3 "Resource3" {
+}
+
+
diff --git a/TestSuite/Misc/testdir b/TestSuite/Misc/testdir
new file mode 100755 (executable)
index 0000000..6e4b5d7
--- /dev/null
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+set errors=0
+
+dirs=`find * -type d -maxdepth 0`
+
+for d in $dirs ; do
+  if [ $d != "CVS" ] ; then
+    cd $d
+    ./runtest
+    errors=$(( $errors + $? ))
+    if [ $? -ne 0 ] ; then
+      echo "Test $d failed"
+    fi
+    cd ..
+  fi
+done
+
+exit $errors
+
index a293fbb..8287845 100755 (executable)
@@ -8,7 +8,11 @@ for d in $dirs ; do
   if [ $d != "CVS" ] ; then
     echo "Testing in $d"
     cd $d
-    ../testdir
+    if [ -x ./testdir ] ; then
+      ./testdir
+    else
+      ../testdir
+    fi
     errors=$(( $errors + $? ))
     cd ..
   fi
index 39e530c..8b22138 100644 (file)
@@ -102,13 +102,20 @@ contain functions as well. Currently there is support for 'istask',
 
 <item>Property reference is now sorted in alphabetical order.
 
-<item>Added missing 'export' report to documentation.
+<item>Added missing 'export' report to documentation. Export reports
+can now contain the scheduled tasks as well as the resource
+allocations.
+
+<item>New keywords 'planbooking' and 'actualbooking' to enter fixed
+bookings of resources in the resource declaration.
 
 <item>Added new example project to illustrate the use of export in big
 projects that are split into sub projects.
 
 <item>HTML comment in HTML report files is now using correct syntax.
 
+<item>Partial fix for correct timezone handling.
+
 </itemize>
 <p>
 
@@ -354,8 +361,11 @@ beginning and is used in relative IDs. A '!' means one level up.
 string may include line breaks.
 
 <label id="date"><tag/DATE/A DATE is an ISO-compliant date in the
-format YYYY-MM-DD[-hh:mm]. Hour, minute,
-and second are optional. If not specified, the values are set to 0.
+format YYYY-MM-DD[-hh:mm[:ss]][-TIMEZONE]. Hour, minutes,
+seconds and the TIMEZONE are optional. If not specified, the values
+are set to 0. The local timezone is used if no other is specified. If
+the timezone is not known taskjuggler will fall back to UTC. See
+the source code (taskjuggler/Utility.cpp) for details. 
 
 <label id="time"><tag/TIME/A time in the format HH:MM.
 
@@ -463,12 +473,24 @@ filename: <ref id="string" name="STRING">
 Optional attributes:
 
 <descrip>
+<tag/hideresource &lt;expression&gt;/
+expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
+
+Report only resources that do not have flags meeting the described logical
+expression.
+
 <tag/hidetask &lt;expression&gt;/
 expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
 
-List only tasks that do not have flags meeting the described logical
+Report only tasks that do not have flags meeting the described logical
 expression.
 
+<tag/rollupresource &lt;expression&gt;/
+expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
+
+Do not show subresources of resources with flags meeting the described
+logical expression.
+
 <tag/rolluptask &lt;expression&gt;/
 expression: <ref id="logicalexpression" name="LOGICALEXPRESSION">
 
@@ -481,6 +503,10 @@ The export report looks like a regular taskjuggler file but contains
 fixed start and end dates for all tasks. The tasks only have start and
 end times and their flags. No other attributes are exported.
 
+If requested the resource usage for the tasks is reported as well. But
+only those allocations are listed that belong to tasks listed in the
+same export report.
+
 The export report can be used to share certain tasks or milestones
 with other projects.
 
index edd2ff3..dc30f20 100644 (file)
@@ -75,9 +75,9 @@ CoreAttributesList::compareItems(QCollection::Item i1, QCollection::Item i2)
        case IdDown:
                return c1->getId() - c2->getId();
        case NameUp:
-               return c2->getName().compare(c1->getName());
-       case NameDown:
                return c1->getName().compare(c2->getName());
+       case NameDown:
+               return c2->getName().compare(c1->getName());
        default:
                qFatal("Please implement sorting for mode %d in sub class!", sorting);
        }
index bab923d..44e97e9 100644 (file)
@@ -40,45 +40,90 @@ ExportReport::generate()
        }
        s.setDevice(&f);
        
-       TaskList filteredList;
-       filterTaskList(filteredList, 0);
-       sortTaskList(filteredList);
+       TaskList filteredTaskList;
+       filterTaskList(filteredTaskList, 0);
+       sortTaskList(filteredTaskList);
 
-       for (Task* t = filteredList.first(); t != 0; t = filteredList.next())
+       ResourceList filteredResourceList;
+       filterResourceList(filteredResourceList, 0);
+       sortResourceList(filteredResourceList);
+
+       generateTaskList(filteredTaskList, filteredResourceList);
+       generateResourceList(filteredTaskList, filteredResourceList);
+       
+       f.close();
+       return TRUE;
+}
+
+bool
+ExportReport::generateTaskList(TaskList& filteredTaskList,
+                                                          ResourceList& filteredResourceList)
+{
+       for (Task* t = filteredTaskList.first(); t != 0;
+                t = filteredTaskList.next())
        {
-               QString start = time2ISO(t->getPlanStart());
-               for (uint i = 0; i < start.length(); i++)
-                       if (start[i] == ' ')
-                               start[i] = '-';
-               QString end = time2ISO(t->getPlanEnd());
-               for (uint i = 0; i < end.length(); i++)
-                       if (end[i] == ' ')
-                               end[i] = '-';
+               QString start = time2tjp(t->getPlanStart());
+               QString end = time2tjp(t->getPlanEnd());
 
                s << "task " << t->getId() << " \"" << t->getName() << "\""
                        << " { start " << start
-                       << " end " << end;
+                       << " end " << end;
                if (showActual)
                {
-                       QString start = time2ISO(t->getActualStart());
-                       for (uint i = 0; i < start.length(); i++)
-                               if (start[i] == ' ')
-                                       start[i] = '-';
-                       QString end = time2ISO(t->getActualEnd());
-                       for (uint i = 0; i < end.length(); i++)
-                               if (end[i] == ' ')
-                                       end[i] = '-';
+                       QString start = time2tjp(t->getActualStart());
+                       QString end = time2tjp(t->getActualEnd());
                        s << "actualStart " << start
                                << " actualEnd " << end;
                }
-               
+
+               if (!filteredResourceList.isEmpty())
+                       s << " projectid " << t->getProjectId() << " ";
                if (t->isMilestone())
                        s << "milestone ";
                
                s << " }" << endl;
        }
 
-       f.close();
+       return TRUE;
+}
+
+bool
+ExportReport::generateResourceList(TaskList& filteredTaskList,
+                                                                  ResourceList& filteredResourceList)
+{
+       for (Resource* r = filteredResourceList.first(); r != 0;
+                r = filteredResourceList.next())
+       {
+               s << "supplement resource " << r->getId() << " {" << endl;
+               BookingList bl = r->getPlanJobs();
+               bl.setAutoDelete(TRUE);
+               for (Booking* b = bl.first(); b != 0; b = bl.next())
+               {
+                       if (filteredTaskList.getTask(b->getTask()->getId()))
+                       {
+                               QString start = time2tjp(b->getStart());
+                               QString end = time2tjp(b->getEnd());
+                               s << "  planbooking " << start << " " << end 
+                                       << " " << b->getProjectId()
+                                       << " " << b->getTask()->getId() << endl;
+                       }
+               }
+               bl = r->getActualJobs();
+               bl.setAutoDelete(TRUE);
+               for (Booking* b = bl.first(); b != 0; b = bl.next())
+               {
+                       if (filteredTaskList.getTask(b->getTask()->getId()))
+                       {
+                               QString start = time2tjp(b->getStart());
+                               QString end = time2tjp(b->getEnd());
+                               s << "  actualbooking " << start << " " << end 
+                                       << " " << b->getProjectId()
+                                       << " " << b->getTask()->getId() << endl;
+                       }
+               }
+               s << "}" << endl;
+       }
+
        return TRUE;
 }
 
index 2e3383e..19ff576 100644 (file)
@@ -23,6 +23,8 @@ public:
        virtual ~ExportReport() { }
 
        bool generate();
+       bool generateTaskList(TaskList& ftl, ResourceList& frl);
+       bool generateResourceList(TaskList& ftl, ResourceList& frl);
 
 private:
        ExportReport() { }
index 470a066..06a0044 100644 (file)
@@ -241,7 +241,7 @@ FileInfo::nextToken(QString& token)
                                token += c;
                        if (c == '-')
                        {
-                               // this must be a ISO date yyyy-mm-dd[[-hh:mm]-TZ]
+                               // this must be a ISO date yyyy-mm-dd[[-hh:mm:[ss]]-TZ]
                                getDateFragment(token, c);
                                if (c != '-')
                                {
@@ -258,6 +258,8 @@ FileInfo::nextToken(QString& token)
                                                return EndOfFile;
                                        }
                                        getDateFragment(token, c);
+                                       if (c == ':')
+                                               getDateFragment(token, c);
                                }
                                int i = 0;
                                if (c == '-')
@@ -559,14 +561,9 @@ ProjectFile::parse()
                        else if (token == KW("vacation"))
                        {
                                time_t from, to;
-                               bool isResourceVacation;
                                QString name;
-                               if (!readVacation(from, to, TRUE, &name,
-                                                                 &isResourceVacation))
+                               if (!readVacation(from, to, TRUE, &name))
                                        return FALSE;
-                               if (isResourceVacation)
-                                       proj->getResource(name)->addVacation(
-                                               new Interval(from, to));
                                proj->addVacation(name, from, to);
                                break;
                        }
@@ -715,7 +712,7 @@ ProjectFile::parse()
 
                                        if ((tt = nextToken(token)) != COMMA)
                                        {
-                                               openFiles.last()->returnToken(tt, token);
+                                               returnToken(tt, token);
                                                break;
                                        }
                                }
@@ -790,7 +787,7 @@ ProjectFile::parse()
 
                                        if ((tt = nextToken(token)) != COMMA)
                                        {
-                                               openFiles.last()->returnToken(tt, token);
+                                               returnToken(tt, token);
                                                break;
                                        }
                                }
@@ -872,6 +869,9 @@ ProjectFile::parse()
 TokenType
 ProjectFile::nextToken(QString& buf)
 {
+       if (openFiles.isEmpty())
+               return EndOfFile;
+
        TokenType tt;
        while ((tt = openFiles.last()->nextToken(buf)) == EndOfFile)
        {
@@ -1159,7 +1159,7 @@ ProjectFile::readTaskBody(Task* task)
                                        task->setScheduling(Task::ASAP);
                                        if ((tt = nextToken(token)) != COMMA)
                                        {
-                                               openFiles.last()->returnToken(tt, token);
+                                               returnToken(tt, token);
                                                break;
                                        }
                                }
@@ -1179,7 +1179,7 @@ ProjectFile::readTaskBody(Task* task)
                                        task->setScheduling(Task::ALAP);
                                        if ((tt = nextToken(token)) != COMMA)
                                        {
-                                               openFiles.last()->returnToken(tt, token);
+                                               returnToken(tt, token);
                                                break;
                                        }
                                }
@@ -1210,7 +1210,7 @@ ProjectFile::readTaskBody(Task* task)
                                        task->addFlag(flag);
                                        if ((tt = nextToken(token)) != COMMA)
                                        {
-                                               openFiles.last()->returnToken(tt, token);
+                                               returnToken(tt, token);
                                                break;
                                        }
                                }
@@ -1293,31 +1293,12 @@ ProjectFile::readTaskBody(Task* task)
 
 bool
 ProjectFile::readVacation(time_t& from, time_t& to, bool readName,
-                                                 QString* n, bool* isResourceVacation)
+                                                 QString* n)
 {
        TokenType tt;
        if (readName)
        {
-               /* If we find a string then we expect a global vacation
-                * definition. If we find an ID then this is an out-of-scope
-                * vacation definition for the resource with this particular
-                * ID. */
-               *isResourceVacation = FALSE;
-               if ((tt = nextToken(*n)) == STRING)
-                       ;       // We don't have to do anything
-#if 0
-               else if (tt == ID)
-               {
-                       if (!proj->getResource(*n))
-                       {
-                               fatalError(QString().sprintf(
-                                       "Resource %s is undefined", n->latin1()));
-                               return FALSE;
-                       }
-                       *isResourceVacation = TRUE;
-               }
-#endif
-               else
+               if ((tt = nextToken(*n)) != STRING)
                {
                        fatalError("String expected");
                        return FALSE;
@@ -1333,7 +1314,7 @@ ProjectFile::readVacation(time_t& from, time_t& to, bool readName,
        if ((tt = nextToken(token)) != MINUS)
        {
                // vacation e. g. 2001-11-28
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
                from = date2time(start);
                to = sameTimeNextDay(date2time(start)) - 1;
        }
@@ -1392,7 +1373,7 @@ ProjectFile::readResource(Resource* parent)
                        return FALSE;
        }
        else
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
 
        proj->addResource(r);
 
@@ -1519,6 +1500,28 @@ ProjectFile::readResourceBody(Resource* r)
                                return FALSE;
                        }
                }
+               else if (token == KW("planbooking"))
+               {
+                       Booking* b;
+                       if ((b = readBooking()) == 0)
+                               return FALSE;
+                       if (!r->addPlanBooking(b))
+                       {
+                               fatalError("Resource is already booked during this period");
+                               return FALSE;
+                       }
+               }
+               else if (token == KW("actualbooking"))
+               {
+                       Booking* b;
+                       if ((b = readBooking()) == 0)
+                               return FALSE;
+                       if (!r->addActualBooking(b))
+                       {
+                               fatalError("Resource is already booked during this period");
+                               return FALSE;
+                       }
+               }
                else if (token == KW("flags"))
                {
                        for ( ; ; )
@@ -1532,7 +1535,7 @@ ProjectFile::readResourceBody(Resource* r)
                                r->addFlag(flag);
                                if ((tt = nextToken(token)) != COMMA)
                                {
-                                       openFiles.last()->returnToken(tt, token);
+                                       returnToken(tt, token);
                                        break;
                                }
                        }
@@ -1619,13 +1622,64 @@ ProjectFile::readShift(Shift* parent)
                }
        }
        else
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
 
        proj->addShift(s);
 
        return TRUE;
 }
 
+Booking*
+ProjectFile::readBooking()
+{
+       QString token;
+
+       if (nextToken(token) != DATE)
+       {
+               fatalError("Start date expected");
+               return 0;
+       }
+       time_t start = date2time(token);
+       if (start < proj->getStart() || start >= proj->getEnd())
+       {
+               fatalError("Start date must be within the project timeframe");
+               return 0;
+       }
+       
+       if (nextToken(token) != DATE)
+       {
+               fatalError("End date expected");
+               return 0;
+       }
+       time_t end = date2time(token);
+       if (end <= proj->getStart() || end > proj->getEnd())
+       {
+               fatalError("End date must be within the project timeframe");
+               return 0;
+       }
+       if (start >= end)
+       {
+               fatalError("End date must be after start date");
+               return 0;
+       }
+
+       QString pid;
+       if (nextToken(pid) != ID || !proj->isValidId(pid))
+       {
+               fatalError("Known project ID expected");
+               return 0;
+       }
+       Task* task;
+       TokenType tt;
+       if (((tt = nextToken(token)) != ID && tt != RELATIVE_ID) ||
+               (task = proj->getTask(token)) == 0)
+       {
+               fatalError("Task ID expected");
+               return 0;
+       }
+       return new Booking(Interval(start, end), task, "", pid);
+}
+
 bool
 ProjectFile::readAccount(Account* parent)
 {
@@ -1719,7 +1773,7 @@ ProjectFile::readAccount(Account* parent)
                }
        }
        else
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
 
        proj->addAccount(a);
 
@@ -1810,12 +1864,12 @@ ProjectFile::readAllocate(Task* t)
                                        }
                                        a->addAlternative(r);
                                } while ((tt = nextToken(token)) == COMMA);
-                               openFiles.last()->returnToken(tt, token);
+                               returnToken(tt, token);
                        }
                }
        }
        else
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
        t->addAllocation(a);
 
        return TRUE;
@@ -2031,7 +2085,7 @@ ProjectFile::readHTMLReport(const QString& reportType)
        TokenType tt;
        if ((tt = nextToken(token)) != LCBRACE)
        {
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
                return TRUE;
        }
 
@@ -2058,7 +2112,7 @@ ProjectFile::readHTMLReport(const QString& reportType)
                                report->addColumn(col);
                                if ((tt = nextToken(token)) != COMMA)
                                {
-                                       openFiles.last()->returnToken(tt, token);
+                                       returnToken(tt, token);
                                        break;
                                }
                        }
@@ -2181,7 +2235,7 @@ ProjectFile::readHTMLAccountReport()
        TokenType tt;
        if ((tt = nextToken(token)) != LCBRACE)
        {
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
                return TRUE;
        }
 
@@ -2208,7 +2262,7 @@ ProjectFile::readHTMLAccountReport()
                                report->addColumn(col);
                                if ((tt = nextToken(token)) != COMMA)
                                {
-                                       openFiles.last()->returnToken(tt, token);
+                                       returnToken(tt, token);
                                        break;
                                }
                        }
@@ -2310,7 +2364,7 @@ ProjectFile::readExportReport()
        TokenType tt;
        if ((tt = nextToken(token)) != LCBRACE)
        {
-               openFiles.last()->returnToken(tt, token);
+               returnToken(tt, token);
                return TRUE;
        }
 
@@ -2340,6 +2394,22 @@ ProjectFile::readExportReport()
                        ExpressionTree* et = new ExpressionTree(op);
                        report->setRollUpTask(et);
                }
+               else if (token == KW("hideresource"))
+               {
+                       Operation* op;
+                       if ((op = readLogicalExpression()) == 0)
+                               return FALSE;
+                       ExpressionTree* et = new ExpressionTree(op);
+                       report->setHideResource(et);
+               }
+               else if (token == KW("rollupresource"))
+               {
+                       Operation* op;
+                       if ((op = readLogicalExpression()) == 0)
+                               return FALSE;
+                       ExpressionTree* et = new ExpressionTree(op);
+                       report->setRollUpResource(et);
+               }
                else
                {
                        fatalError("Illegal attribute");
@@ -2549,26 +2619,42 @@ ProjectFile::readSorting(Report* report, int which)
 time_t
 ProjectFile::date2time(const QString& date)
 {
-       int y, m, d, hour, min;
+       int y, m, d, hour, min, sec;
        char tZone[16] = "";
-       if (sscanf(date, "%d-%d-%d-%d:%d-%s", &y, &m, &d, &hour, &min, tZone) == 6)
-               ;
+       char savedTZ[16] = "";
+       if (sscanf(date, "%d-%d-%d-%d:%d:%d-%s",
+                          &y, &m, &d, &hour, &min, &sec, tZone) == 7 ||
+               (sec = 0) ||    // set sec to 0
+               sscanf(date, "%d-%d-%d-%d:%d-%s",
+                          &y, &m, &d, &hour, &min, tZone) == 6)
+       {
+               if (getenv("TZ"))
+                       strcpy(getenv("TZ"), savedTZ);
+               const char* tz;
+               if ((tz = timezone2tz(tZone)) == 0)
+                       fatalError(QString("Illegal timezone %s").arg(tZone));
+               else
+                       setenv("TZ", tz, 1);
+       }
+       else if (sscanf(date, "%d-%d-%d-%d:%d:%d",
+                                       &y, &m, &d, &hour, &min, &sec) == 6)
+               tZone[0] = '\0';
        else if (sscanf(date, "%d-%d-%d-%d:%d", &y, &m, &d, &hour, &min) == 5)
+       {
+               sec = 0;
                tZone[0] = '\0';
+       }
        else if (sscanf(date, "%d-%d-%d", &y, &m, &d) == 3)
-       {                       
+       {
                tZone[0] = '\0';
-               hour = min = 0;
+               hour = min = sec = 0;
        }
-
-       char savedTZ[16] = "";
-       if (strcmp(tZone, "") != 0)
+       else
        {
-               if (getenv("TZ"))
-                       strcpy(getenv("TZ"), savedTZ);
-               setenv("TZ", tZone, 1);
+               qFatal("Illegal date: %s", date.latin1());
+               return 0;
        }
-       
+
        if (y < 1970)
        {
                fatalError("Year must be larger than 1969");
@@ -2585,7 +2671,7 @@ ProjectFile::date2time(const QString& date)
                d = 1;
        }
 
-       struct tm t = { 0, min, hour, d, m - 1, y - 1900, 0, 0, -1, 0, 0 };
+       struct tm t = { sec, min, hour, d, m - 1, y - 1900, 0, 0, -1, 0, 0 };
        time_t localTime = mktime(&t);
 
        if (strcmp(savedTZ, "") != 0) 
index 24cea21..6a4735d 100644 (file)
@@ -79,7 +79,8 @@ public:
        TokenType nextToken(QString& token);
        void returnToken(TokenType t, const QString& buf)
        {
-               openFiles.last()->returnToken(t, buf);
+               if (!openFiles.isEmpty())
+                       openFiles.last()->returnToken(t, buf);
        }
 
        const QString& getFile() { return openFiles.last()->getFile(); }
@@ -102,9 +103,10 @@ private:
        bool readResourceSupplement();
        bool readResourceBody(Resource* r);
        bool readVacation(time_t& from, time_t& to, bool readName = FALSE,
-                                         QString* = 0, bool* isResourceVacation = 0);
+                                         QString* = 0);
        bool readAccount(Account* parent);
        bool readShift(Shift* parent);
+       Booking* readBooking();
        bool readCredit(Account* a);
        bool readAllocate(Task* t);
        bool readPlanTimeFrame(Task* t, double& d);
index 2117d37..0620a58 100644 (file)
@@ -1152,6 +1152,7 @@ ReportHtml::planSchedule(Resource* r, Task* t)
 
        bool first = TRUE;
        BookingList planJobs = r->getPlanJobs();
+       planJobs.setAutoDelete(TRUE);
        for (Booking* b = planJobs.first(); b != 0; b = planJobs.next())
        {
                if (t == b->getTask())
@@ -1177,6 +1178,7 @@ ReportHtml::actualSchedule(Resource* r, Task* t)
 
        bool first = TRUE;
        BookingList actualJobs = r->getActualJobs();
+       actualJobs.setAutoDelete(TRUE);
        for (Booking* b = actualJobs.first(); b != 0; b = actualJobs.next())
        {
                if (t == b->getTask())
index 4bc8ca7..8ed9483 100644 (file)
@@ -143,7 +143,7 @@ Resource::~Resource()
        if (planScoreboard)
        {
                for (uint i = 0; i < sbSize; i++)
-                       if (planScoreboard[i] > (Booking*) 3)
+                       if (planScoreboard[i] > (SbBooking*) 3)
                        {
                                uint j;
                                for (j = i + 1; j < sbSize &&
@@ -157,7 +157,7 @@ Resource::~Resource()
        if (actualScoreboard)
        {
                for (uint i = 0; i < sbSize; i++)
-                       if (actualScoreboard[i] > (Booking*) 3)
+                       if (actualScoreboard[i] > (SbBooking*) 3)
                        {
                                uint j;
                                for (j = i + 1; j < sbSize &&
@@ -175,11 +175,11 @@ Resource::~Resource()
 void
 Resource::initScoreboard()
 {
-       scoreboard = new (Booking*)[sbSize];
+       scoreboard = new (SbBooking*)[sbSize];
 
        // First mark all scoreboard slots as unavailable.
        for (uint i = 0; i < sbSize; i++)
-               scoreboard[i] = (Booking*) 1;
+               scoreboard[i] = (SbBooking*) 1;
 
        // Then change all worktime slots to 0 (available) again.
        for (time_t day = project->getStart(); day < project->getEnd();
@@ -210,7 +210,7 @@ Resource::initScoreboard()
                        for (time_t date = interval.getStart();
                                 date < interval.getEnd() && date < project->getEnd();
                                 date += project->getScheduleGranularity())
-                               scoreboard[sbIndex(date)] = (Booking*) 0;
+                               scoreboard[sbIndex(date)] = (SbBooking*) 0;
                }
        }
 
@@ -220,7 +220,7 @@ Resource::initScoreboard()
                         date < i->getEnd() &&
                                 project->getStart() <= date && date < project->getEnd();
                         date += project->getScheduleGranularity())
-                       scoreboard[sbIndex(date)] = (Booking*) 2;
+                       scoreboard[sbIndex(date)] = (SbBooking*) 2;
 
        // Mark all global vacation slots as such (2)
        for (Interval* i = project->getVacationListFirst(); i != 0;
@@ -230,7 +230,7 @@ Resource::initScoreboard()
                         date < i->getEnd() &&
                                 project->getStart() <= date && date < project->getEnd();
                         date += project->getScheduleGranularity())
-                       scoreboard[sbIndex(date)] = (Booking*) 2;
+                       scoreboard[sbIndex(date)] = (SbBooking*) 2;
        }
 }
 
@@ -241,12 +241,27 @@ Resource::sbIndex(time_t date) const
        uint sbIdx = (date - project->getStart()) /
                project->getScheduleGranularity();
        if (sbIdx < 0 || sbIdx >= sbSize)
-               qFatal("Date %s out of range (Resource %s, Index %d)",
+               qFatal("Date %s is outside of the defined project timeframe "
+                          "(Resource %s, Index %d)",
                           time2ISO(date).latin1(),
                           id.latin1(), sbIdx);
        return sbIdx;
 }
 
+time_t
+Resource::index2start(uint idx) const
+{
+       return project->getStart() + idx *
+               project->getScheduleGranularity();
+}
+
+time_t
+Resource::index2end(uint idx) const
+{
+       return project->getStart() + (idx + 1) *
+               project->getScheduleGranularity() - 1;
+}
+
 void
 Resource::getSubResourceList(ResourceList& rl)
 {
@@ -308,8 +323,8 @@ Resource::isAvailable(time_t date, time_t duration, int loadFactor, Task* t)
        
        for (uint i = sbStart; i < sbEnd; i++)
        {
-               Booking* b = scoreboard[i];
-               if (b < (Booking*) 4)
+               SbBooking* b = scoreboard[i];
+               if (b < (SbBooking*) 4)
                        continue;
 
                bookedTime += duration;
@@ -322,35 +337,97 @@ Resource::isAvailable(time_t date, time_t duration, int loadFactor, Task* t)
                <= (loadFactor / 100.0);
 }
 
-void
+bool
 Resource::book(Booking* nb)
 {
-       uint index = sbIndex(nb->getStart());
+       uint idx = sbIndex(nb->getStart());
 
-       Booking* b;
+       return bookSlot(idx, nb);
+}
+
+bool
+Resource::bookSlot(uint idx, SbBooking* nb)
+{
+       // Test if the time slot is still available.
+       if (scoreboard[idx] != 0)
+               return FALSE;
+       
+       SbBooking* b;
        // Try to merge the booking with the booking in the previous slot.
-       if (index > 0 && (b = scoreboard[index - 1]) > (Booking*) 3 &&
+       if (idx > 0 && (b = scoreboard[idx - 1]) > (SbBooking*) 3 &&
                b->getTask() == nb->getTask() &&
                b->getProjectId() == nb->getProjectId())
        {
-               if (!b->getInterval().append(nb->getInterval()))
-                       qFatal("Cannot append time slot");
-               scoreboard[index] = b;
+               scoreboard[idx] = b;
                delete nb;
-               return;
+               return TRUE;
        }
        // Try to merge the booking with the booking in the following slot.
-       if (index < sbSize - 1 && (b = scoreboard[index + 1]) > (Booking*) 3 &&
+       if (idx < sbSize - 1 && (b = scoreboard[idx + 1]) > (SbBooking*) 3 &&
                b->getTask() == nb->getTask() &&
                b->getProjectId() == nb->getProjectId())
        {
-               if (!b->getInterval().prepend(nb->getInterval()))
-                       qFatal("Cannot prepend time slot");
-               scoreboard[index] = b;
+               scoreboard[idx] = b;
                delete nb;
-               return;
+               return TRUE;
        }
-       scoreboard[index] = nb;
+       scoreboard[idx] = nb;
+       return TRUE;
+}
+
+bool
+Resource::bookInterval(Booking* nb)
+{
+       uint sIdx = sbIndex(nb->getStart());
+       uint eIdx = sbIndex(nb->getEnd());
+
+       for (uint i = sIdx; i <= eIdx; i++)
+               if (scoreboard[i])
+               {
+                       qWarning("Resource %s is already booked at %s",
+                                        id.latin1(), time2ISO(index2start(i)).latin1());
+                       return FALSE;
+               }
+       for (uint i = sIdx; i <= eIdx; i++)
+               bookSlot(i, new SbBooking(*nb));
+
+       return TRUE;
+}
+
+bool
+Resource::addPlanBooking(Booking* nb)
+{
+       SbBooking** tmp = scoreboard;
+       
+       if (planScoreboard)
+               scoreboard = planScoreboard;
+       else
+               initScoreboard();
+       bool retVal = bookInterval(nb);
+       // Cross register booking with task.
+       if (retVal && nb->getTask())
+               nb->getTask()->addPlanBookedResource(this);
+       planScoreboard = scoreboard;
+       scoreboard = tmp;
+       return retVal;
+}
+
+bool
+Resource::addActualBooking(Booking* nb)
+{
+       SbBooking** tmp = scoreboard;
+       
+       if (actualScoreboard)
+               scoreboard = actualScoreboard;
+       else
+               initScoreboard();
+       bool retVal = bookInterval(nb);
+       // Cross register booking with task.
+       if (retVal && nb->getTask())
+               nb->getTask()->addActualBookedResource(this);
+       actualScoreboard = scoreboard;
+       scoreboard = tmp;
+       return retVal;
 }
 
 double
@@ -369,8 +446,8 @@ Resource::getPlanLoad(const Interval& period, Task* task)
        for (uint i = sbIndex(iv.getStart());
                 i < sbIndex(iv.getEnd()) && i < sbSize; i++)
        {
-               Booking* b = planScoreboard[i];
-               if (b < (Booking*) 4)
+               SbBooking* b = planScoreboard[i];
+               if (b < (SbBooking*) 4)
                        continue;
                if (task == 0 || task == b->getTask())
                        bookedTime += project->getScheduleGranularity();
@@ -392,15 +469,16 @@ Resource::getActualLoad(const Interval& period, Task* task)
        for (Resource* r = subFirst(); r != 0; r = subNext())
                subLoad += r->getActualLoad(iv, task);
 
-       for (uint i = sbIndex(iv.getStart());
-                i < sbIndex(iv.getEnd()) && i < sbSize; i++)
-       {
-               Booking* b = actualScoreboard[i];
-               if (b < (Booking*) 4)
-                       continue;
-               if (task == 0 || task == b->getTask())
-                       bookedTime += project->getScheduleGranularity();
-       }
+       if (actualScoreboard)
+               for (uint i = sbIndex(iv.getStart());
+                        i < sbIndex(iv.getEnd()) && i < sbSize; i++)
+               {
+                       SbBooking* b = actualScoreboard[i];
+                       if (b < (SbBooking*) 4)
+                               continue;
+                       if (task == 0 || task == b->getTask())
+                               bookedTime += project->getScheduleGranularity();
+               }
 
        return project->convertToDailyLoad(bookedTime) * efficiency + subLoad;
 }
@@ -430,8 +508,8 @@ Resource::getPlanPIDs(const Interval& period, Task* task, QStringList& pids)
        for (uint i = sbIndex(iv.getStart());
                 i < sbIndex(iv.getEnd()) && i < sbSize; i++)
        {
-               Booking* b = planScoreboard[i];
-               if (b < (Booking*) 4)
+               SbBooking* b = planScoreboard[i];
+               if (b < (SbBooking*) 4)
                        continue;
                if ((task == 0 || task == b->getTask()) &&
                        pids.findIndex(b->getProjectId()) == -1)
@@ -464,18 +542,19 @@ Resource::getActualPIDs(const Interval& period, Task* task, QStringList& pids)
        for (Resource* r = subFirst(); r != 0; r = subNext())
                r->getActualPIDs(iv, task, pids);
 
-       for (uint i = sbIndex(iv.getStart());
-                i < sbIndex(iv.getEnd()) && i < sbSize; i++)
-       {
-               Booking* b = actualScoreboard[i];
-               if (b < (Booking*) 4)
-                       continue;
-               if ((task == 0 || task == b->getTask()) &&
-                       pids.findIndex(b->getProjectId()) == -1)
+       if (actualScoreboard)
+               for (uint i = sbIndex(iv.getStart());
+                        i < sbIndex(iv.getEnd()) && i < sbSize; i++)
                {
-                       pids.append(b->getProjectId());
+                       SbBooking* b = actualScoreboard[i];
+                       if (b < (SbBooking*) 4)
+                               continue;
+                       if ((task == 0 || task == b->getTask()) &&
+                               pids.findIndex(b->getProjectId()) == -1)
+                       {
+                               pids.append(b->getProjectId());
+                       }
                }
-       }
 }
 
 QString
@@ -521,15 +600,26 @@ BookingList
 Resource::getPlanJobs()
 {
        BookingList bl;
-
-       Booking* b = 0;
-       for (uint i = 0; i < sbSize; i++)
-               if (planScoreboard[i] > (Booking*) 3 && planScoreboard[i] != b)
-               {
-                       bl.append(planScoreboard[i]);
-                       b = planScoreboard[i];
-               }
-
+       if (planScoreboard)
+       {
+               SbBooking* b = 0;
+               uint startIdx = 0;
+               for (uint i = 0; i < sbSize; i++)
+                       if (planScoreboard[i] != b)
+                       {
+                               if (b)
+                                       bl.append(new Booking(Interval(index2start(startIdx),
+                                                                                                  index2end(i - 1)),
+                                                                                 planScoreboard[startIdx]));
+                               if (planScoreboard[i] > (SbBooking*) 3)
+                               {
+                                       b = planScoreboard[i];
+                                       startIdx = i;
+                               }
+                               else
+                                       b = 0;
+                       }
+       }
        return bl;
 }
 
@@ -537,14 +627,26 @@ BookingList
 Resource::getActualJobs()
 {
        BookingList bl;
-
-       Booking* b = 0;
-       for (uint i = 0; i < sbSize; i++)
-               if (actualScoreboard[i] > (Booking*) 3 && actualScoreboard[i] != b)
-               {
-                       bl.append(actualScoreboard[i]);
-                       b = actualScoreboard[i];
-               }
+       if (actualScoreboard)
+       {
+               SbBooking* b = 0;
+               uint startIdx = 0;
+               for (uint i = 0; i < sbSize; i++)
+                       if (actualScoreboard[i] != b)
+                       {
+                               if (b)
+                                       bl.append(new Booking(Interval(index2start(startIdx),
+                                                                                                  index2end(i - 1)),
+                                                                                 actualScoreboard[startIdx]));
+                               if (actualScoreboard[i] > (SbBooking*) 3)
+                               {
+                                       b = actualScoreboard[i];
+                                       startIdx = i;
+                               }
+                               else
+                                       b = 0;
+                       }
+       }
 
        return bl;
 }
@@ -552,7 +654,10 @@ Resource::getActualJobs()
 void
 Resource::preparePlan()
 {
-       initScoreboard();
+       if (planScoreboard)
+               scoreboard = planScoreboard;
+       else
+               initScoreboard();
 }
 
 void
@@ -564,7 +669,10 @@ Resource::finishPlan()
 void
 Resource::prepareActual()
 {
-       initScoreboard();
+       if (actualScoreboard)
+               scoreboard = actualScoreboard;
+       else
+               initScoreboard();
 }
 
 void
index a02607b..6a3fc51 100644 (file)
 
 class Project;
 class Task;
+class Resource;
 
-class Booking
+class SbBooking
+{
+public:
+       SbBooking(Task* t, QString a = "", QString i = "")
+               : task(t), account(a), projectId(i) { }
+       ~SbBooking() { }
+       
+       Task* getTask() const { return task; }
+
+       void setAccount(const QString a) { account = a; }
+       const QString& getAccount() const { return account; }
+
+       void setProjectId(const QString i) { projectId = i; }
+       const QString& getProjectId() const { return projectId; }
+
+private:
+       // A pointer to the task that caused the booking
+       Task* task;
+       // String identifying the KoTrus account the effort is credited to.
+       QString account;
+       // The Project ID
+       QString projectId;
+};
+
+class Booking : public SbBooking
 {
 public:
        Booking(const Interval& iv, Task* t, QString a = "",
                        QString i = "")
-               : interval(iv), task(t), account(a), projectId(i) { }
+               : SbBooking(t, a, i), interval(iv) { }
+       Booking(const Interval& iv, SbBooking* sb) : SbBooking(*sb),
+                       interval(iv) { }
        ~Booking() { }
 
        time_t getStart() const { return interval.getStart(); }
@@ -39,14 +66,6 @@ public:
        time_t getDuration() const { return interval.getDuration(); }
        Interval& getInterval() { return interval; }
 
-       Task* getTask() const { return task; }
-
-       void setAccount(const QString a) { account = a; }
-       const QString& getAccount() const { return account; }
-
-       void setProjectId(const QString i) { projectId = i; }
-       const QString& getProjectId() const { return projectId; }
-
        void setLockTS( const QString& ts ) { lockTS = ts; }
        const QString& getLockTS() const { return lockTS; }
 
@@ -56,21 +75,11 @@ public:
 private:
        // The booked time period.
        Interval interval;
-       // A pointer to the task that caused the booking
-       Task* task;
-       // String identifying the KoTrus account the effort is credited to.
-       QString account;
-       // The Project ID
-       QString projectId;
-   
        // The database lock timestamp
        QString lockTS;
 
        // the lockers ID
        QString lockerId;
-
-       // A list of flags that can be used to select a sub-set of tasks.
-       FlagList flagList;
 } ;
 
 class BookingList : public QPtrList<Booking>
@@ -150,7 +159,12 @@ public:
 
        bool isAvailable(time_t day, time_t duration, int loadFactor, Task* t);
 
-       void book(Booking* b);
+       bool book(Booking* b);
+
+       bool bookSlot(uint idx, SbBooking* nb);
+       bool bookInterval(Booking* b);
+       bool addPlanBooking(Booking* b);
+       bool addActualBooking(Booking* b);
 
        double getPlanLoad(const Interval& i, Task* task = 0);
 
@@ -199,6 +213,9 @@ private:
        void initScoreboard();
        uint sbIndex(time_t date) const;
 
+       time_t index2start(uint idx) const;
+       time_t index2end(uint idx) const;
+
        /// Pointer used by subResourceFirst() and subResourceNext().
        Resource* currentSR;
 
@@ -238,14 +255,14 @@ private:
         * For each time slot (of length scheduling granularity) we store a
         * pointer to a booking, a '1' if slot is off-hours, a '2' if slot is
         * during a vacation or 0 if resource is available. */
-       Booking** scoreboard;
+       SbBooking** scoreboard;
        /// The number of time slots in the project.
        uint sbSize;
 
        /// Scoring of planned usages of the resource.
-       Booking** planScoreboard;
+       SbBooking** planScoreboard;
        /// Scoring of actual usages of the resource.
-       Booking** actualScoreboard;
+       SbBooking** actualScoreboard;
 } ;
 
 #endif
index c483aad..2c6c9f3 100644 (file)
@@ -1197,6 +1197,7 @@ Task::preparePlan()
        lastSlot = 0;
        schedulingDone = FALSE;
        bookedResources.clear();
+       bookedResources = planBookedResources;
 
        if (actualStart == 0.0)
                actualStart = planStart;
@@ -1233,6 +1234,7 @@ Task::prepareActual()
        lastSlot = 0;
        schedulingDone = FALSE;
        bookedResources.clear();
+       bookedResources = actualBookedResources;
 }
 
 void
index 3f5bbd2..1a8d788 100644 (file)
@@ -208,6 +208,11 @@ public:
 
        double getPlanLoad(const Interval& period, Resource* resource = 0);
 
+       void addPlanBookedResource(Resource* r)
+       {
+               if (planBookedResources.find(r) == -1)
+                       planBookedResources.inSort(r);
+       }
        bool isPlanBookedResource(Resource* r)
        {
                return planBookedResources.find(r) != -1;
@@ -252,6 +257,11 @@ public:
        bool isActualActive(const Interval& period) const;
 
        double getActualLoad(const Interval& period, Resource* r = 0);
+       void addActualBookedResource(Resource* r)
+       {
+               if (actualBookedResources.find(r) == -1)
+                       actualBookedResources.inSort(r);
+       }
        bool isActualBookedResource(Resource* r)
        {
                return actualBookedResources.find(r) != -1;
@@ -336,7 +346,6 @@ private:
                if (bookedResources.find(r) == -1)
                        bookedResources.inSort(r);
        }
-
        time_t earliestStart();
        time_t latestEnd();
        void fatalError(const QString& msg) const;
index 4510c83..a503adc 100644 (file)
 #include <stdio.h>
 #include "Utility.h"
 
+#include <qdict.h>
+
+static QDict<const char> TZDict;
+static bool TZDictReady = FALSE;
+
+const char*
+timezone2tz(const char* tzone)
+{
+       if (!TZDictReady)
+       {
+               TZDict.insert("PST", "GMP8:00");
+               TZDict.insert("PDT", "GMT7:00");
+               TZDict.insert("MST", "GMT7:00");
+               TZDict.insert("MDT", "GMT6:00");
+               TZDict.insert("CST", "GMT6:00");
+               TZDict.insert("CDT", "GMT5:00");
+               TZDict.insert("EST", "GMT5:00");
+               TZDict.insert("EDT", "GMT4:00");
+               TZDict.insert("CET", "GMT-1:00");
+               TZDict.insert("CEST", "GMT-2:00");
+
+               TZDictReady = TRUE;
+       }
+
+       return TZDict[tzone];
+}
+
 const char*
 monthAndYear(time_t t)
 {
@@ -256,6 +283,15 @@ QString time2ISO(time_t t)
        return buf;
 }
 
+QString time2tjp(time_t t)
+{
+       struct tm* tms = localtime(&t);
+       static char buf[128];
+
+       strftime(buf, 127, "%Y-%m-%d-%H:%M:%S-%Z", tms);
+       return buf;
+}
+
 QString time2time(time_t t)
 {
        struct tm* tms = localtime(&t);
index 7d59fe9..7e2deb0 100644 (file)
@@ -20,6 +20,8 @@
 #define ONEDAY (60 * 60 * 24)
 #define ONEHOUR (60 * 60)
 
+const char* timezone2tz(const char* tzone);
+
 const char* monthAndYear(time_t d);
 
 int daysLeftInMonth(time_t d);
@@ -83,6 +85,8 @@ int dayOfWeek(time_t d);
 
 QString time2ISO(time_t t);
 
+QString time2tjp(time_t t);
+
 QString time2time(time_t t);
 
 QString time2date(time_t t);