Vim window logic, slimv

This commit is contained in:
Ian Keane 2020-02-24 20:27:04 -05:00
parent babcc9e44b
commit 515847d07e
791 changed files with 51552 additions and 86 deletions

View file

@ -0,0 +1,22 @@
*.*fas*
*.fasl
*.dfsl
*.ppcf
*.x86f
*.fas
*.lib
doc/manual/
doc/*.vr
doc/*.tp
doc/*.tps
doc/*.toc
doc/*.pg
doc/*.log
doc/*.ky
doc/*.it
doc/*.info
doc/*.fn
doc/*.fns
doc/*.cp
doc/*.cps
doc/*.aux

View file

@ -0,0 +1,20 @@
local-time Copyright (c) 2005-2012 by Daniel Lowe
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,17 @@
Thanks to those of you who have helped me make LOCAL-TIME an engaging
and worthwhile project!
* Matthew Danish <mdanish@andrew.cmu.edu>
* The #lisp crew on irc.freenode.net
* Vladimir Sekissov <svg@surnet.ru>
* Attila Lendvai <attila.lendvai@gmail.com>
* Tomi Borbely <tomi.borbely@gmail.com>
* Denys Rtveliashvili <rtvd@mail.ru>
* Levente Meszaros <levente.meszaros@gmail.com>
* Arjan Wekking <arjan@streamtech.nl>
------------------------------------------------------------------------
Apologies to anyone I didn't mention (please let me know).
Daniel Lowe
<dlowe@dlowe.net>

View file

@ -0,0 +1,40 @@
# Installation
## Using Quicklisp
1. Install quicklisp
2. Use quicklisp to install local-time
(ql:quickload :local-time)
## Manual Installation
1. Make sure the ASDF library is loaded. In SBCL, this involves:
(require :asdf)
2. Add the path to the directory containing local-time.asd to
asdf:*central-registry*. If you've installed this package using
asdf-install, this shouldn't be necessary.
(push "/my/local-time/directory/" asdf:*central-registry*)
3. Load local-time using ASDF:
(asdf:oos 'asdf:load-op 'local-time)
4. In your own projects, you can direct asdf to automatically load
LOCAL-TIME with the :depends-on asdf:defsystem directive.
(defsystem myproject
:depends-on (local-time)
...)
## Testing
local-time uses Stefil for testing. To run the Stefil test suite, do
the following:-
(ql:quickload :local-time.test)
(in-suite test)
(test)

View file

@ -0,0 +1,18 @@
LOCAL-TIME
See the "COPYING" file for information about legal issues of copying
and using this library.
See the "INSTALL" file for information about building and installing
this library.
The LOCAL-TIME library is a Common Lisp library for the manipulation
of dates and times. It is based almost entirely upon Erik Naggum's
paper "The Long Painful History of Time".
Related projects of interest:
* https://github.com/enaeher/local-time-duration
* http://common-lisp.net/projects/cl-l10n/
See the manual/ directory for more information and/or the website at
http://common-lisp.net/project/local-time/

View file

@ -0,0 +1,104 @@
-*- mode: org; coding: utf-8-unix -*-
* Basic Outline
** A more human friendly parser
PARSE-TIMESTRING works fine, but we need a more human-friendly parser that
can handle stuff like "08/03/2006 1:54pm CST". or maybe not? see
http://chaitanyagupta.com/lisp/chronicity/
** Support for other notational systems than the Gregorian
** Ideally, local-time should have everything that the ruby Date class does
Also, the time functions in the venerable and honored clsql library.
This may be a tall order.
** Integrate local-time with http://common-lisp.net/project/cl-l10n/
Also drop time/timezone related functionality from cl-l10n.
** Make local-time work with olson timezone rules
http://www.twinsun.com/tz/tz-link.htm
Right now, we're using zic compiled files, which have a limited
scope. They may also be larger than the corresponding rule files.
* Attila:
Many of the operations currently defined on timestamps should not
really be defined on timestamps at all. i think this is due to some
heritage from the times when a timestamp value also had a timezone
value attached to it.
Examples of such bogus operations are timestamp+, timestamp-, all
adjust-timestamp variants that operate on the components of a date
value, etc.
* Nomenclature
** timestamp: a sharp cut on the continuous t axis (as in physics) of time
It is mostly unrelated to timezones and different calendar systems
(e.g. Julian, Gregorian), which are just ways to denote a timestamp in
a way humans can easily deal with.
** interval: a range on the time axis denoted by two timestamps
** date-time: contains the fields defined by the calendar system
Some date-time fields may be left undefined.
** calendar: a converter to and from a particular date-time type
A method defining how to denote a timestamp using a set of numbers
(called year, month, day, etc). there are various such encodings
like the Gregorian calendar, or the Julian calendar (which was in
effect in Russia until 1922!). wikipedia has loads of info about
these. We'll default to a naive Gregorian calendar
** duration: a value representing a delta between date-times
* Timestamp Operations
** basic arithmetic
essentially arithmetic on numbers denoting seconds. operations that
would expose the epoch chosen by the implementation are not allowed
(e.g. adding two timestamps), but things like adding/subbing
literals and substracting two timestamps are valid.
** convert to/from date
(only in the mandatory context of a calendar system, which most of
the time includes a timezone, too.)
** create an interval given two timestamps
** comparison predicates
* Interval Operations
There's a project implementing intervals at:
https://github.com/enaeher/local-time-duration
** Arithmetic with timestamps
** Arithmetic with intervals
** convert to a duration with a calendar
** Intersection with a timestamp
** Overlap with another intervals
* Date-time operations
** parse from string representation
, e.g. an rfc3339 string. note that such a date-time does not
necessarily describe a timestamp completely. it only does so when
it contains all the components needed by the calendar system to
denote a sharp cut on the t axis.
** set/offset any component of it,
although what those operations mean potentially depend on the
calendar system and its parameters (e.g. the optional timezone
value in it). also note that some operations are potentially
undefined and signal an error with certain illegal values
(e.g. instantiating a date using an illegal or contradictory
combination of its parameters; setting a field to an illegal value
like february 29 in leap years...)
** convert to/from a timestamp
(strictly in the context of a calendar system and a timezone)
** comparison functions
* Duration operations
** Arithmetic with date-times
** Arithmetic with durations

View file

@ -0,0 +1,9 @@
(defsystem #:cl-postgres+local-time
:name "cl-postgres+local-time"
:version "1.0.6"
:author "Daniel Lowe <dlowe@dlowe.net>"
:description "Integration between cl-postgres and local-time"
:depends-on (:cl-postgres :local-time)
:components ((:module "src"
:components ((:module "integration"
:components ((:file "cl-postgres")))))))

View file

@ -0,0 +1,16 @@
# -*- Mode: Makefile; tab-width: 4; indent-tabs-mode: t -*-
all: docs
docs:
sh gendocs.sh -o manual --html "--css-include=style.css" local-time "The local-time Manual"
clean:
find . \( -name "*.info" -o -name "*.it" -o -name "*.aux" -o -name "*.cp" -o -name "*.fn" -o -name "*.fns" -o -name "*.ky" -o -name "*.log" -o -name "*.pg" -o -name "*.toc" -o -name "*.tp" -o -name "*.vr" -o -name "*.dvi" -o -name "*.cps" -o -name "*.vrs" \) -exec rm {} \;
rm -rf manual
upload-docs:
rsync -av --delete -e ssh manual common-lisp.net:/project/local-time/public_html/
# scp -r manual spec common-lisp.net:/project/local-time/public_html/
# vim: ft=make ts=4 noet

View file

@ -0,0 +1,285 @@
#!/bin/sh
# gendocs.sh -- generate a GNU manual in many formats. This script is
# mentioned in maintain.texi. See the help message below for usage details.
# $Id: gendocs.sh,v 1.17 2006/05/19 00:07:52 karl Exp $
#
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you can either send email to this
# program's maintainer or write to: The Free Software Foundation,
# Inc.; 51 Franklin Street, Fifth Floor; Boston, MA 02110-1301, USA.
#
# Original author: Mohit Agarwal.
# Send bug reports and any other correspondence to bug-texinfo@gnu.org.
prog=`basename "$0"`
srcdir=`pwd`
scripturl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs.sh"
templateurl="http://savannah.gnu.org/cgi-bin/viewcvs/~checkout~/texinfo/texinfo/util/gendocs_template"
: ${MAKEINFO="makeinfo"}
: ${TEXI2DVI="texi2dvi -t @finalout"}
: ${DVIPS="dvips"}
: ${DOCBOOK2TXT="docbook2txt"}
: ${DOCBOOK2HTML="docbook2html"}
: ${DOCBOOK2PDF="docbook2pdf"}
: ${DOCBOOK2PS="docbook2ps"}
: ${GENDOCS_TEMPLATE_DIR="."}
unset CDPATH
rcs_revision='$Revision: 1.17 $'
rcs_version=`set - $rcs_revision; echo $2`
program=`echo "$0" | sed -e 's!.*/!!'`
version="gendocs.sh $rcs_version
Copyright (C) 2006 Free Software Foundation, Inc.
There is NO warranty. You may redistribute this software
under the terms of the GNU General Public License.
For more information about these matters, see the files named COPYING."
usage="Usage: $prog [OPTION]... PACKAGE MANUAL-TITLE
Generate various output formats from PACKAGE.texinfo (or .texi or .txi) source.
See the GNU Maintainers document for a more extensive discussion:
http://www.gnu.org/prep/maintain_toc.html
Options:
-o OUTDIR write files into OUTDIR, instead of manual/.
--docbook convert to DocBook too (xml, txt, html, pdf and ps).
--html ARG pass indicated ARG to makeinfo for HTML targets.
--help display this help and exit successfully.
--version display version information and exit successfully.
Simple example: $prog emacs \"GNU Emacs Manual\"
Typical sequence:
cd YOURPACKAGESOURCE/doc
wget \"$scripturl\"
wget \"$templateurl\"
$prog YOURMANUAL \"GNU YOURMANUAL - One-line description\"
Output will be in a new subdirectory \"manual\" (by default, use -o OUTDIR
to override). Move all the new files into your web CVS tree, as
explained in the Web Pages node of maintain.texi.
MANUAL-TITLE is included as part of the HTML <title> of the overall
manual/index.html file. It should include the name of the package being
documented. manual/index.html is created by substitution from the file
$GENDOCS_TEMPLATE_DIR/gendocs_template. (Feel free to modify the
generic template for your own purposes.)
If you have several manuals, you'll need to run this script several
times with different YOURMANUAL values, specifying a different output
directory with -o each time. Then write (by hand) an overall index.html
with links to them all.
You can set the environment variables MAKEINFO, TEXI2DVI, and DVIPS to
control the programs that get executed, and GENDOCS_TEMPLATE_DIR to
control where the gendocs_template file is looked for.
Email bug reports or enhancement requests to bug-texinfo@gnu.org.
"
calcsize()
{
size=`ls -ksl $1 | awk '{print $1}'`
echo $size
}
outdir=manual
html=
PACKAGE=
MANUAL_TITLE=
while test $# -gt 0; do
case $1 in
--help) echo "$usage"; exit 0;;
--version) echo "$version"; exit 0;;
-o) shift; outdir=$1;;
--docbook) docbook=yes;;
--html) shift; html=$1;;
-*)
echo "$0: Unknown or ambiguous option \`$1'." >&2
echo "$0: Try \`--help' for more information." >&2
exit 1;;
*)
if test -z "$PACKAGE"; then
PACKAGE=$1
elif test -z "$MANUAL_TITLE"; then
MANUAL_TITLE=$1
else
echo "$0: extra non-option argument \`$1'." >&2
exit 1
fi;;
esac
shift
done
if test -s "$srcdir/$PACKAGE.texinfo"; then
srcfile=$srcdir/$PACKAGE.texinfo
elif test -s "$srcdir/$PACKAGE.texi"; then
srcfile=$srcdir/$PACKAGE.texi
elif test -s "$srcdir/$PACKAGE.txi"; then
srcfile=$srcdir/$PACKAGE.txi
else
echo "$0: cannot find .texinfo or .texi or .txi for $PACKAGE in $srcdir." >&2
exit 1
fi
if test ! -r $GENDOCS_TEMPLATE_DIR/gendocs_template; then
echo "$0: cannot read $GENDOCS_TEMPLATE_DIR/gendocs_template." >&2
echo "$0: it is available from $templateurl." >&2
exit 1
fi
echo Generating output formats for $srcfile
cmd="${MAKEINFO} -o $PACKAGE.info \"$srcfile\""
echo "Generating info files... ($cmd)"
eval "$cmd"
mkdir -p $outdir/
tar czf $outdir/$PACKAGE.info.tar.gz $PACKAGE.info*
info_tgz_size=`calcsize $outdir/$PACKAGE.info.tar.gz`
# do not mv the info files, there's no point in having them available
# separately on the web.
cmd="${TEXI2DVI} \"$srcfile\""
echo "Generating dvi ... ($cmd)"
eval "$cmd"
# now, before we compress dvi:
echo Generating postscript...
${DVIPS} $PACKAGE -o
gzip -f -9 $PACKAGE.ps
ps_gz_size=`calcsize $PACKAGE.ps.gz`
mv $PACKAGE.ps.gz $outdir/
# compress/finish dvi:
gzip -f -9 $PACKAGE.dvi
dvi_gz_size=`calcsize $PACKAGE.dvi.gz`
mv $PACKAGE.dvi.gz $outdir/
cmd="${TEXI2DVI} --pdf \"$srcfile\""
echo "Generating pdf ... ($cmd)"
eval "$cmd"
pdf_size=`calcsize $PACKAGE.pdf`
mv $PACKAGE.pdf $outdir/
cmd="${MAKEINFO} -o $PACKAGE.txt --no-split --no-headers \"$srcfile\""
echo "Generating ASCII... ($cmd)"
eval "$cmd"
ascii_size=`calcsize $PACKAGE.txt`
gzip -f -9 -c $PACKAGE.txt >$outdir/$PACKAGE.txt.gz
ascii_gz_size=`calcsize $outdir/$PACKAGE.txt.gz`
mv $PACKAGE.txt $outdir/
cmd="${MAKEINFO} --no-split --html -o $PACKAGE.html $html \"$srcfile\""
echo "Generating monolithic html... ($cmd)"
rm -rf $PACKAGE.html # in case a directory is left over
eval "$cmd"
html_mono_size=`calcsize $PACKAGE.html`
gzip -f -9 -c $PACKAGE.html >$outdir/$PACKAGE.html.gz
html_mono_gz_size=`calcsize $outdir/$PACKAGE.html.gz`
mv $PACKAGE.html $outdir/
cmd="${MAKEINFO} --html -o $PACKAGE.html $html \"$srcfile\""
echo "Generating html by node... ($cmd)"
eval "$cmd"
split_html_dir=$PACKAGE.html
(
cd ${split_html_dir} || exit 1
tar -czf ../$outdir/${PACKAGE}.html_node.tar.gz -- *.html
)
html_node_tgz_size=`calcsize $outdir/${PACKAGE}.html_node.tar.gz`
rm -f $outdir/html_node/*.html
mkdir -p $outdir/html_node/
mv ${split_html_dir}/*.html $outdir/html_node/
rmdir ${split_html_dir}
echo Making .tar.gz for sources...
srcfiles=`ls *.texinfo *.texi *.txi *.eps 2>/dev/null`
tar cvzfh $outdir/$PACKAGE.texi.tar.gz $srcfiles
texi_tgz_size=`calcsize $outdir/$PACKAGE.texi.tar.gz`
if test -n "$docbook"; then
cmd="${MAKEINFO} -o - --docbook \"$srcfile\" > ${srcdir}/$PACKAGE-db.xml"
echo "Generating docbook XML... $(cmd)"
eval "$cmd"
docbook_xml_size=`calcsize $PACKAGE-db.xml`
gzip -f -9 -c $PACKAGE-db.xml >$outdir/$PACKAGE-db.xml.gz
docbook_xml_gz_size=`calcsize $outdir/$PACKAGE-db.xml.gz`
mv $PACKAGE-db.xml $outdir/
cmd="${DOCBOOK2HTML} -o $split_html_db_dir ${outdir}/$PACKAGE-db.xml"
echo "Generating docbook HTML... ($cmd)"
eval "$cmd"
split_html_db_dir=html_node_db
(
cd ${split_html_db_dir} || exit 1
tar -czf ../$outdir/${PACKAGE}.html_node_db.tar.gz -- *.html
)
html_node_db_tgz_size=`calcsize $outdir/${PACKAGE}.html_node_db.tar.gz`
rm -f $outdir/html_node_db/*.html
mkdir -p $outdir/html_node_db
mv ${split_html_db_dir}/*.html $outdir/html_node_db/
rmdir ${split_html_db_dir}
cmd="${DOCBOOK2TXT} ${outdir}/$PACKAGE-db.xml"
echo "Generating docbook ASCII... ($cmd)"
eval "$cmd"
docbook_ascii_size=`calcsize $PACKAGE-db.txt`
mv $PACKAGE-db.txt $outdir/
cmd="${DOCBOOK2PS} ${outdir}/$PACKAGE-db.xml"
echo "Generating docbook PS... $(cmd)"
eval "$cmd"
gzip -f -9 -c $PACKAGE-db.ps >$outdir/$PACKAGE-db.ps.gz
docbook_ps_gz_size=`calcsize $outdir/$PACKAGE-db.ps.gz`
mv $PACKAGE-db.ps $outdir/
cmd="${DOCBOOK2PDF} ${outdir}/$PACKAGE-db.xml"
echo "Generating docbook PDF... ($cmd)"
eval "$cmd"
docbook_pdf_size=`calcsize $PACKAGE-db.pdf`
mv $PACKAGE-db.pdf $outdir/
fi
echo Writing index file...
curdate=`date '+%B %d, %Y'`
sed \
-e "s!%%TITLE%%!$MANUAL_TITLE!g" \
-e "s!%%DATE%%!$curdate!g" \
-e "s!%%PACKAGE%%!$PACKAGE!g" \
-e "s!%%HTML_MONO_SIZE%%!$html_mono_size!g" \
-e "s!%%HTML_MONO_GZ_SIZE%%!$html_mono_gz_size!g" \
-e "s!%%HTML_NODE_TGZ_SIZE%%!$html_node_tgz_size!g" \
-e "s!%%INFO_TGZ_SIZE%%!$info_tgz_size!g" \
-e "s!%%DVI_GZ_SIZE%%!$dvi_gz_size!g" \
-e "s!%%PDF_SIZE%%!$pdf_size!g" \
-e "s!%%PS_GZ_SIZE%%!$ps_gz_size!g" \
-e "s!%%ASCII_SIZE%%!$ascii_size!g" \
-e "s!%%ASCII_GZ_SIZE%%!$ascii_gz_size!g" \
-e "s!%%TEXI_TGZ_SIZE%%!$texi_tgz_size!g" \
-e "s!%%DOCBOOK_HTML_NODE_TGZ_SIZE%%!$html_node_db_tgz_size!g" \
-e "s!%%DOCBOOK_ASCII_SIZE%%!$docbook_ascii_size!g" \
-e "s!%%DOCBOOK_PS_GZ_SIZE%%!$docbook_ps_gz_size!g" \
-e "s!%%DOCBOOK_PDF_SIZE%%!$docbook_pdf_size!g" \
-e "s!%%DOCBOOK_XML_SIZE%%!$docbook_xml_size!g" \
-e "s!%%DOCBOOK_XML_GZ_SIZE%%!$docbook_xml_gz_size!g" \
-e "s,%%SCRIPTURL%%,$scripturl,g" \
-e "s!%%SCRIPTNAME%%!$prog!g" \
$GENDOCS_TEMPLATE_DIR/gendocs_template >$outdir/index.html
echo "Done! See $outdir/ subdirectory for new files."

View file

@ -0,0 +1,260 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!-- $Id: gendocs_template,v 1.7 2005/05/15 00:00:08 karl Exp $ -->
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<!--
This template was adapted from Texinfo:
http://savannah.gnu.org/cgi-bin/viewcvs/texinfo/texinfo/util/gendocs_template
-->
<head>
<title>%%TITLE%%</title>
<meta http-equiv="content-type" content='text/html; charset=utf-8' />
<!-- <link rel="stylesheet" type="text/css" href="/gnu.css" /> -->
<!-- <link rev="made" href="webmasters@gnu.org" /> -->
<style>
/* CSS style taken from http://gnu.org/gnu.css */
html, body {
background-color: #FFFFFF;
color: #000000;
font-family: sans-serif;
}
a:link {
color: #1f00ff;
background-color: transparent;
text-decoration: underline;
}
a:visited {
color: #9900dd;
background-color: transparent;
text-decoration: underline;
}
a:hover {
color: #9900dd;
background-color: transparent;
text-decoration: none;
}
.center {
text-align: center;
}
.italic {
font-style: italic;
}
.bold {
font-weight: bold;
}
.quote {
margin-left: 40px;
margin-right: 40px;
}
.hrsmall {
width: 80px;
height: 1px;
margin-left: 20px;
}
.td_title {
border-color: #3366cc;
border-style: solid;
border-width: thin;
color: #3366cc;
background-color : #f2f2f9;
font-weight: bold;
}
.td_con {
padding-top: 3px;
padding-left: 8px;
padding-bottom: 3px;
color : #303030;
background-color : #fefefe;
font-size: smaller;
}
.translations {
background-color: transparent;
color: black;
font-family: serif;
font-size: smaller;
}
.fsflink {
font-size: smaller;
font-family: monospace;
color : #000000;
border-left: #3366cc thin solid;
border-bottom: #3366cc thin solid;
padding-left: 5px;
padding-bottom: 5px;
}
/*
* rtl stands for right-to-left layout, as in farsi/persian,
* arabic, etc. See also trans_rtl.
*/
.fsflink_rtl {
font-size: smaller;
font-family: monospace;
color : #000000;
border-right: #3366cc thin solid;
border-bottom: #3366cc thin solid;
padding-right: 5px;
padding-bottom: 5px;
}
.trans {
font-size: smaller;
color : #000000;
border-left: #3366cc thin solid;
padding-left: 20px;
}
.trans_rtl {
font-size: smaller;
color : #000000;
border-right: #3366cc thin solid;
padding-right: 20px;
}
img {
border: none 0;
}
td.side {
color: #3366cc;
/* background: #f2f2f9;
border-color: #3366cc;
border-style: solid;
border-width: thin; */
border-color: white;
border-style: none;
vertical-align: top;
width: 150px;
}
div.copyright {
font-size: 80%;
border: 2px solid #3366cc;
padding: 4px;
background: #f2f2f9;
border-style: solid;
border-width: thin;
}
.footnoteref {
font-size: smaller;
vertical-align: text-top;
}
</style>
</head>
<!-- This document is in XML, and xhtml 1.0 -->
<!-- Please make sure to properly nest your tags -->
<!-- and ensure that your final document validates -->
<!-- consistent with W3C xhtml 1.0 and CSS standards -->
<!-- See validator.w3.org -->
<body>
<h3>%%TITLE%%</h3>
<!-- <address>Free Software Foundation</address> -->
<address>last updated %%DATE%%</address>
<!--
<p>
<a href="/graphics/gnu-head.jpg">
<img src="/graphics/gnu-head-sm.jpg"
alt=" [image of the head of a GNU] "
width="129" height="122" />
</a>
<a href="/philosophy/gif.html">(no gifs due to patent problems)</a>
</p>
-->
<hr />
<p>This document <!--(%%PACKAGE%%)--> is available in the following formats:</p>
<ul>
<li><a href="%%PACKAGE%%.html">HTML
(%%HTML_MONO_SIZE%%K characters)</a> - entirely on one web page.</li>
<li><a href="html_node/index.html">HTML</a> - with one web page per
node.</li>
<li><a href="%%PACKAGE%%.html.gz">HTML compressed
(%%HTML_MONO_GZ_SIZE%%K gzipped characters)</a> - entirely on
one web page.</li>
<li><a href="%%PACKAGE%%.html_node.tar.gz">HTML compressed
(%%HTML_NODE_TGZ_SIZE%%K gzipped tar file)</a> -
with one web page per node.</li>
<li><a href="%%PACKAGE%%.info.tar.gz">Info document
(%%INFO_TGZ_SIZE%%K characters gzipped tar file)</a>.</li>
<li><a href="%%PACKAGE%%.txt">ASCII text
(%%ASCII_SIZE%%K characters)</a>.</li>
<li><a href="%%PACKAGE%%.txt.gz">ASCII text compressed
(%%ASCII_GZ_SIZE%%K gzipped characters)</a>.</li>
<li><a href="%%PACKAGE%%.dvi.gz">TeX dvi file
(%%DVI_GZ_SIZE%%K characters gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.ps.gz">PostScript file
(%%PS_GZ_SIZE%%K characters gzipped)</a>.</li>
<li><a href="%%PACKAGE%%.pdf">PDF file
(%%PDF_SIZE%%K characters)</a>.</li>
<li><a href="%%PACKAGE%%.texi.tar.gz">Texinfo source
(%%TEXI_TGZ_SIZE%%K characters gzipped tar file)</a></li>
</ul>
<p>(This page was generated by the <a href="%%SCRIPTURL%%">%%SCRIPTNAME%%
script</a>.)</p>
<div class="copyright">
<p>
Return to <a href="/project/local-time/">local-time's home page</a>.
</p>
<!--
<p>
Please send FSF &amp; GNU inquiries to
<a href="mailto:gnu@gnu.org"><em>gnu@gnu.org</em></a>.
There are also <a href="/home.html#ContactInfo">other ways to contact</a>
the FSF.
<br />
Please send broken links and other corrections (or suggestions) to
<a href="mailto:webmasters@gnu.org"><em>webmasters@gnu.org</em></a>.
</p>
-->
<p>
<!--
Copyright (C) 2008
Daniel Lowe &lt;dlowe dlowe.net&gt
Attila Lendvai &lt;attila.lendvai gmail.com&gt;
<br />
Verbatim copying and distribution of this entire article is
permitted in any medium, provided this notice is preserved.
-->
</p>
<p>
Updated: %%DATE%%
<!-- timestamp start -->
<!-- $Date: 2005/05/15 00:00:08 $ $Author: karl $ -->
<!-- timestamp end -->
</p>
</div>
</body>
</html>

View file

@ -0,0 +1,774 @@
\input texinfo @c -*- Mode: Texinfo; Mode: auto-fill -*-
@c %**start of header
@setfilename local-time.info
@settitle The local-time Manual
@exampleindent 2
@c @documentencoding utf-8
@macro name {}
@code{local-time}
@end macro
@macro mathx {tex, non-tex}
@iftex
@math{\tex\}
@end iftex
@ifnottex
@emph{\non-tex\}
@end ifnottex
@end macro
@macro impnote {text}
@quotation Implementor's note
@emph{\text\}
@end quotation
@end macro
@c Set ROMANCOMMENTS to get comments in roman font.
@ifset ROMANCOMMENTS
@alias lispcmt = r
@end ifset
@ifclear ROMANCOMMENTS
@alias lispcmt = asis
@end ifclear
@c Index for public api.
@defindex it
@macro pub {name}
@itindex \name\
@c
@end macro
@macro k {what}
@code{\what\}
@end macro
@iftex
@alias v = asis
@alias cl = code
@end iftex
@ifnottex
@alias v = var
@alias cl = strong
@end ifnottex
@c Show variables, clauses, and concepts in the same index.
@syncodeindex it cp
@syncodeindex vr cp
@copying
Copyright @copyright{} 2012 Daniel Lowe <dlowe dlowe.net> @*
Copyright @copyright{} 2012 Attila Lendvai <attila.lendvai gmail.com> @*
@quotation
This manual describes the @name{} Common Lisp library which is
based on Erik Naggum's @emph{The Long, Painful History of Time}
[NaggumPaper] paper.
@end quotation
@end copying
@c %**end of header
@titlepage
@title The @name{} Manual
@subtitle Version 1.0.0
@author Daniel Lowe
@author Attila Lendvai
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage
@contents
@ifnottex
@node Top
@top @name{}
@insertcopying
@end ifnottex
@menu
* Introduction::
* Public API::
* Other Features::
* References::
* Comprehensive Index::
@end menu
@c ===================================================================
@node Introduction
@chapter Introduction
@menu
* Portability::
@end menu
The @name{} library is a Common Lisp library for the manipulation of
dates, times and intervals. It was originally based almost entirely
upon Erik Naggum's paper @emph{The Long Painful History of Time}
[NaggumPaper]. Many of the core concepts originated from this paper,
such as the seperation of days and seconds, the choice of 2000-03-01
as the standard epoch, and the timestring format.
@c ===================================================================
@node Portability
@section Portability
This implementation assumes that time zone information is stored in
the tzfile format. The default timezone is loaded from
/etc/localtime. On non-POSIX systems, this will certainly give
different results than the system time handling.
local-time currently supports sub-second precision clocks with ABCL,
Allegro, CMUCL, CCL, SBCL, and LispWorks for Linux or Darwin. All
others will be able to retrieve the time with second precision using
@code{get-universal-time}. You may add support for your own
implementation by implementing the clock generic protocol documented
here.
@c ===================================================================
@node Public API
@chapter Public API
@menu
* Types::
* Timezones::
* Creating timestamp Objects::
* Querying timestamp Objects::
* Manipulating Date and Time Values::
* Parsing and Formatting::
* Clocks::
@end menu
@c ===================================================================
@node Types
@section Types
It's a good idea to treat all values as immutable objects. @name{}
will not modify any object it was given unless explicitly asked to by
the @code{:into} keyword argument.
@itindex timestamp
@deftp Class timestamp day sec nsec
@code{timestamp} values can represent either a @emph{date},
a @emph{daytime} or a @emph{time} value. It has the following slots:
@lisp
(defclass timestamp ()
((day :type integer)
(sec :type integer)
(nsec :type (integer 0 999999999))))
@end lisp
The following constraints apply to the specific types:
@itemize
@item
@emph{date}: must have a @var{+utc-zone+} timezone and the @var{sec}
slot must be the first second of a day; In other words, the time
elements of the @code{timestamp} value must have their least possible
values.
@item
@emph{time}: the @var{day} slot must be zero.
@end itemize
@end deftp
@deftp Struct timezone path name loaded
@code{timezone} objects represent timezones - local and political
modifications to the time representation. Timezones are responsible
for storing offsets from GMT, abbreviations for different
sub-timezones, and the times each sub-timezone is to be in effect.
@end deftp
@c ===================================================================
@node Timezones
@section Timezones
@itindex *default-timezone*
@defvr Default *default-timezone*
The variable @var{*default-timezone*} contains the timezone that will
be used by default if none is specified. It is loaded from
@emph{/etc/localtime} when the library is loaded. If
@emph{/etc/localtime} is not present, it will default to UTC.
@end defvr
@itindex +utc-zone+
@defvr Constant +utc-zone+
The variable @var{+utc-zone+} contains a timezone corresponding to
UTC.
@end defvr
@itindex define-timezone
@defmac define-timezone zone-name zone-file &key (load nil)
Define @var{zone-name} (a symbol or a string) as a new timezone,
lazy-loaded from @var{zone-file} (a pathname designator relative to
the zoneinfo directory on this system. If @var{load} is true, load
immediately.
@end defmac
@itindex find-timezone-by-location-name
@defun find-timezone-by-location-name name
Returns the timezone found at the location name (such as
@code{US/Eastern}). @code{reread-timezone-repository} must be called
before this function is used.
@end defun
@itindex reread-timezone-repository
@defun reread-timezone-repository &key (timezone-repository *default-timezone-repository-path*)
Walks the current repository, reading all tzinfo files updating
indexes. The default timezone repository is set to the zoneinfo/
directory of the local-time system.
@end defun
@c ===================================================================
@node Creating timestamp Objects
@section Creating @code{timestamp} Objects
@itindex universal-to-timestamp
@defun universal-to-timestamp universal &key (nsec 0)
Produces a @code{timestamp} instance from the provided universal time
@var{universal}. Universal time is defined in the Common Lisp
Specification as the number of seconds since 1900-01-01T00:00:00Z.
@end defun
@itindex unix-to-timestamp
@defun unix-to-timestamp unix &key (nsec 0)
Produces a @code{timestamp} instance from the provided unix time
@var{unix}. Unix time is defined by POSIX as the number of seconds
since 1970-01-01T00:00:00Z.
@end defun
@itindex now
@defun now
Produces a @code{timestamp} instance with the current time. With
Allegro, CMUCL, CCL, SBCL, and LispWorks for Linux or Darwin, the new
timestamp will be precise to the microsecond (usec); with ABCL, to the
millisecond (ms). Otherwise, the precision is limited to the second.
@end defun
@itindex today
@defun today
Produces a @code{timestamp} instance that corresponds to today's
date, which is the midnight of the current day in the UTC zone.
@end defun
@itindex encode-timestamp
@defun encode-timestamp nsec sec minute hour day month year &key timezone offset into
Returns a new @code{timestamp} instance corresponding to the specified
time elements. The @var{offset} is the number of seconds offset from
UTC of the locale. If @var{offset} is not specified, the offset will
be guessed from the @var{timezone}. If a @code{timestamp} is passed
as the @var{into} argument, its value will be set and that
@code{timestamp} will be returned. Otherwise, a new @code{timestamp}
is created.
@end defun
@itindex make-timestamp
@defmac make-timestamp &key :day :sec :nsec
Expands to an expression that creates an instance of a
@code{timestamp} exactly as specified.
@end defmac
@itindex clone-timestamp
@defmac clone-timestamp timestamp
Expands to an expression that creates another copy of @var{timestamp}
that is @code{timestamp=} to it.
@end defmac
@c ===================================================================
@node Querying timestamp Objects
@section Querying @code{timestamp} Objects
@itindex day-of
@defun day-of timestamp
Returns the day component of @var{timestamp}. Although Naggum's paper
specifies that the day should be a signed fixnum, it is left unbounded
for flexibility reasons.
@end defun
@itindex sec-of
@defun sec-of timestamp
Returns the 'seconds' component of the time. Valid values for the
seconds range from 0 to 86399.
@end defun
@itindex nsec-of
@defun nsec-of timestamp
Returns the 'microseconds' component of the time. Valid values for
the nanoseconds range from 0 to 999999999.
@end defun
@itindex timestamp-to-universal
@defun timestamp-to-universal timestamp
This returns the date/time specified in @var{timestamp} encoded as
the number of seconds since January 1st, 1900 12:00am UTC.
@end defun
@itindex timestamp-to-unix
@defun timestamp-to-unix timestamp
This returns the date/time specified in @var{timestamp} encoded as
the number of seconds since January 1st, 1970 12:00am UTC. It
corresponds with the time received from the POSIX call @code{time()}.
@end defun
@itindex timestamp-subtimezone
@defun timestamp-subtimezone timestamp timezone
Returns as multiple values the time zone applicable at the given time
as the number of seconds east of UTC, a boolean daylight-saving-p, and
the customary abbreviation of the timezone.
@end defun
@itindex with-decoded-timestamp
@defmac with-decoded-timestamp (&key nsec sec minute hour day month year day-of-week daylight-p timezone) timestamp &body body
This macro binds variables to the decoded elements of @var{timestamp}.
The timezone argument is used for decoding the timestamp, and is not
bound by the macro. The value of @var{day-of-week} starts from 0 which
means Sunday.
@end defmac
@itindex decode-timestamp
@defun decode-timestamp timestamp
Returns the decoded time as @code{(values ns ss mm hh day month
year day-of-week daylight-saving-time-p timezone-offset timezone-abbreviation)}.
@end defun
@itindex timestamp<
@itindex timestamp<=
@itindex timestamp>
@itindex timestamp>=
@itindex timestamp=
@itindex timestamp/=
@defun timestamp< time-a time-b
@defunx timestamp<= time-a time-b
@defunx timestamp> time-a time-b
@defunx timestamp>= time-a time-b
@defunx timestamp= time-a time-b
@defunx timestamp/= time-a time-b
These comparison functions act like their string and char counterparts.
@end defun
@itindex timestamp-minimum
@defun timestamp-minimum timestamp &rest timestamps
Returns the earliest timestamp passed to it.
@end defun
@itindex timestamp-maximum
@defun timestamp-maximum timestamp &rest timestamps
Returns the latest timestamp passed to it.
@end defun
@itindex timestamp-day-of-week
@defun timestamp-day-of-week timestamp
This returns the index of the day of the week,
starting at 0 which means Sunday.
@quotation Note
''Day of the week'' is ambigous and locale dependent.
@end quotation
@end defun
@itindex universal-to-timestamp
@defun universal-to-timestamp timestamp
Returns the UNIVERSAL-TIME corresponding to @var{timestamp}.
@quotation Note
Subsecond precision is not preserved.
@end quotation
@end defun
@itindex timestamp-millennium
@itindex timestamp-century
@itindex timestamp-decade
@defun timestamp-millennium timestamp &key timezone
@defunx timestamp-century timestamp &key timezone
@defunx timestamp-decade timestamp &key timezone
Returns the ordinal millennium, century or decade upon which the
timestamp falls. Ordinal time values start at 1, so the
(timestamp-century (now)) will return 21.
@end defun
@itindex timestamp-year
@itindex timestamp-month
@itindex timestamp-day
@itindex timestamp-hour
@itindex timestamp-minute
@itindex timestamp-second
@itindex timestamp-millisecond
@itindex timestamp-microsecond
@defun timestamp-year timestamp &key timezone
@defunx timestamp-month timestamp &key timezone
@defunx timestamp-day timestamp &key timezone
@defunx timestamp-hour timestamp &key timezone
@defunx timestamp-minute timestamp &key timezone
@defunx timestamp-second timestamp &key timezone
@defunx timestamp-millisecond timestamp &key timezone
@defunx timestamp-microsecond timestamp &key timezone
@defunx timestamp-microsecond timestamp &key timezone
Returns the decoded part of the timestamp.
@end defun
@c ===================================================================
@node Manipulating Date and Time Values
@section Manipulating Date and Time Values
@itindex timestamp+
@itindex timestamp-
@defun timestamp+ time amount unit
@defunx timestamp- time amount unit
Add or subtract the @var{amount} to the @var{time} using the specified
@var{unit}. @var{unit} may be one of ( @code{:nsec} @code{:sec}
@code{:minute} @code{:hour} @code{:day} @code{:month} @code{:year}).
The value of the parts of the timestamp of higher resolution than the
UNIT will never be touched. If you want a precise number of seconds
from a time, you should specify the offset in seconds.
@end defun
@itindex timestamp-maximize-part
@defun timestamp-maximize-part timestamp part &key offset timezone into
Returns a timestamp with its parts maximized up to @var{part}. @var{part} can be
any of (:nsec :sec :min :hour :day :month). If @var{into} is specified, it
will be modified and returned, otherwise a new timestamp will be
created.
@end defun
@itindex timestamp-minimize-part
@defun timestamp-minimize-part timestamp part &key offset timezone into
Returns a timestamp with its parts minimized up to @var{part}. @var{part} can be
any of (:nsec :sec :min :hour :day :month). If @var{into} is specified, it
will be modified and returned, otherwise a new timestamp will be
created.
@end defun
@itindex adjust-timestamp
@defmac adjust-timestamp timestamp &body changes
Alters various parts of @var{timestamp}, given a list of changes. The
changes are in the format @code{(offset part value)} and @code{(set
part value)}.
@lisp
;; Return a new @code{timestamp} value that points to the Monday in
;; the week of @code{(today)}
(adjust-timestamp (today) (offset :day-of-week :monday))
;; Return a new @code{timestamp} value that points three days ahead from now
(adjust-timestamp (today) (offset :day 3))
@end lisp
Keep in mind that @code{adjust-timestamp} is not a mere setter for
fields but instead it handles overflows and timezone conversions as
expected. Also note that it's possible to specify multiple commands.
The list of possible places to manipulate are: @code{:nsec}
@code{:sec} @code{:sec-of-day} @code{:minute} @code{:hour}
@code{:day} @code{:day-of-month} @code{:month} @code{:year}.
@end defmac
@itindex adjust-timestamp!
@defmac adjust-timestamp! timestamp &body changes
Just like @code{adjust-timestamp}, but instead of returning a freshly
constructed value, it alters the provided @var{timestamp} value (and
returns it).
@end defmac
@itindex timestamp-whole-year-difference
@defun timestamp-whole-year-difference time-a time-b
Returns the number of whole years elapsed between @var{time-a} and @var{time-b}.
@quotation Note
This is useful for calculating anniversaries and birthdays.
@end quotation
@end defun
@itindex days-in-month
@defun days-in-month month year
Returns the number of days in a given month of the specified year.
@end defun
@c ===================================================================
@node Parsing and Formatting
@section Parsing and Formatting
@itindex +iso-8601-format+
@defvr Constant +iso-8601-format+
The constant @var{+iso-8601-format+} is bound to a description of the ISO 8601 format. An output with this format will look like this: @samp{2008-03-01T19:42:34.608506+01:00}. This is the default format for the @code{format-timestring} function.
@end defvr
@itindex +asctime-format+
@defvr Constant +asctime-format+
The constant @var{+asctime-format+} is bound to a format mirroring the output of the POSIX asctime() function. An output with this format will look like this: @samp{Sat Mar 1 19:42:34 2008}.
@end defvr
@itindex +rfc-1123-format+
@defvr Constant +rfc-1123-format+
The constant @var{+rfc-1123-format+} is bound to a description of the format defined in RFC 1123 for Internet timestamps. An output with this format will look like this: @samp{Sat, 01 Mar 2008 19:42:34 -0500}.
@end defvr
@itindex +iso-week-date-format+
@defvr Constant +iso-week-date-format+
The constant @var{+iso-week-date-format+} is bound to a description of the ISO 8601 Week Date format. An output with this format will look like this: @samp{2009-W53-5}.
@end defvr
@itindex parse-timestring
@defun parse-timestring timestring &key (start 0) end (fail-on-error t) (offset 0)
Parses a timestring and returns the corresponding @code{timestamp}.
Parsing begins at @var{start} and stops at the @var{end}
position. If there are invalid characters within @code{timestring}
and @var{fail-on-error} is @code{T}, then an @code{invalid-timestring}
error is signaled, otherwise @code{NIL} is returned.
If there is no timezone specified in @code{timestring} then
@var{offset} is used as the default timezone offset (in seconds).
@end defun
@itindex format-timestring
@defun format-timestring (destination timestamp &key (format +iso-8601-format+) (timezone *default-timezone*))
Constructs a string representation of TIMESTAMP according to FORMAT and returns it. If destination is @code{T}, the string is written to @code{*standard-output*}. If destination is a stream, the string is written to the stream.
FORMAT is a list containing one or more of strings, characters, and keywords. Strings and characters are output literally, while keywords are replaced by the values here:
@table @code
@item :year
*year
@item :month
*numeric month
@item :day
*day of month
@item :weekday
*numeric day of week, starting from 0 which means Sunday
@item :hour
*hour
@item :min
*minutes
@item :sec
*seconds
@item :msec
*milliseconds
@item :usec
*microseconds
@item :nsec
*nanoseconds
@item :iso-week-year
*year for ISO week date (can be different from regular calendar year)
@item :iso-week-number
*ISO week number (i.e. 1 through 53)
@item :iso-week-day
*ISO compatible weekday number (i.e. monday=1, sunday=7)
@item :ordinal-day
day of month as an ordinal (e.g. 1st, 23rd)
@item :long-weekday
long form of weekday (e.g. Sunday, Monday)
@item :short-weekday
short form of weekday (e.g. Sun, Mon)
@item :minimal-weekday
minimal form of weekday (e.g. Su, Mo)
@item :short-year
short form of year (last 2 digits, e.g. 41, 42 instead of 2041, 2042)
@item :long-month
long form of month (e.g. January, February)
@item :short-month
short form of month (e.g. Jan, Feb)
@item :hour12
hour on a 12-hour clock
@item :ampm
am/pm marker in lowercase
@item :gmt-offset
the gmt-offset of the time, in +00:00 form
@item :gmt-offset-or-z
like :gmt-offset, but is Z when UTC
@item :gmt-offset-hhmm
like :gmt-offset, but in +0000 form
@item :timezone
timezone abbrevation for the time
@end table
Elements marked by * can be placed in a list in the form:
@lisp
(:keyword padding &optional (padchar #\0))
@end lisp
The string representation of the value will be padded with the padchar.
You can see examples by examining the values in @var{+iso-8601-format+}, @var{+asctime-format+}, and @var{+rfc-1123-format+}.
Produces on @var{stream} the timestring corresponding to the @var{timestamp} with
the given options. If @var{stream} is @code{nil}, only returns a string containing what
would have been the output. If @var{stream} is @code{t}, prints the string to
@var{*standard-output*}.
Example output:
@lisp
LOCAL-TIME> (format-timestring nil (now))
"2008-03-01T19:42:34.608506+01:00"
@end lisp
@end defun
@itindex format-rfc3339-timestring
@defun format-rfc3339-timestring (destination timestamp &key omit-date-part omit-time-part omit-timezone-part (use-zulu t))
Formats the time like format-timestring, but in RFC 3339 format. The options control valid options in the RFC.
@end defun
@itindex format-rfc1123-timestring
@defun format-rfc1123-timestring (destination timestamp &key (timezone *default-timezone*))
Formats the time like format-timestring, but in RFC 1123 format.
@end defun
@c ===================================================================
@node Clocks
@section Clocks
@defvr Default *clock*
The *clock* special variable and the following generic functions are
exposed so that applications may re-define the current time or date as
required. This can be used for testing or to support alternate clocks.
The currently supported values are:
@itemize
@item @code{t} - Use the standard system clock with no adjustments
@item @code{leap-second-adjusted} - The system clock, adjusted for leap seconds using the information in *default-timezone*.
@end itemize
@end defvr
@defun clock-now (clock)
Specialize this generic function to re-define the present moment
@end defun
@defun clock-today (clock)
Specialize this generic function to re-define the present day
@end defun
@c ===================================================================
@node Other Features
@chapter Other Features
@section Reader Macros
@itindex enable-read-macros
@defun enable-read-macros
Adds @@TIMESTRING and #@@UNIVERSAL-TIME as reader macros.
@end defun
@section Support for non-Gregorian Calendars
@itindex astronomical-julian-date
@defun astronomical-julian-date timestamp
Returns the julian date of the date portion of @var{timestamp}.
@end defun
@itindex modified-julian-date
@defun astronomical-julian-date timestamp
Returns the modified julian date of the date portion of @var{timestamp}.
@end defun
@c ===================================================================
@node References
@chapter References
@itemize
@item
[NaggumPaper] Erik Naggum. @emph{The Long Painful History of Time}
@url{http://naggum.no/lugm-time.html}, 1999.
@end itemize
@c ===================================================================
@node Comprehensive Index
@unnumbered Index
@printindex cp
@bye

View file

@ -0,0 +1,48 @@
body {font-family: century schoolbook, serif;
line-height: 1.3;
padding-left: 5em; padding-right: 1em;
padding-bottom: 1em; max-width: 60em;}
table {border-collapse: collapse}
span.roman { font-family: century schoolbook, serif; font-weight: normal; }
h1, h2, h3, h4, h5, h6 {font-family: Helvetica, sans-serif}
/*h4 {padding-top: 0.75em;}*/
dfn {font-family: inherit; font-variant: italic; font-weight: bolder }
kbd {font-family: monospace; text-decoration: underline}
/*var {font-family: Helvetica, sans-serif; font-variant: slanted}*/
var {font-variant: slanted;}
td {padding-right: 1em; padding-left: 1em}
sub {font-size: smaller}
.node {padding: 0; margin: 0}
.lisp { font-family: monospace;
background-color: #F4F4F4; border: 1px solid #AAA;
padding-top: 0.5em; padding-bottom: 0.5em; }
/* coloring */
.lisp-bg { background-color: #F4F4F4 ; color: black; }
.lisp-bg:hover { background-color: #F4F4F4 ; color: black; }
.symbol { font-weight: bold; color: #770055; background-color : transparent; border: 0px; margin: 0px;}
a.symbol:link { font-weight: bold; color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
a.symbol:active { font-weight: bold; color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
a.symbol:visited { font-weight: bold; color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
a.symbol:hover { font-weight: bold; color : #229955; background-color : transparent; text-decoration: none; border: 0px; margin: 0px; }
.special { font-weight: bold; color: #FF5000; background-color: inherit; }
.keyword { font-weight: bold; color: #770000; background-color: inherit; }
.comment { font-weight: normal; color: #007777; background-color: inherit; }
.string { font-weight: bold; color: #777777; background-color: inherit; }
.character { font-weight: bold; color: #0055AA; background-color: inherit; }
.syntaxerror { font-weight: bold; color: #FF0000; background-color: inherit; }
span.paren1 { font-weight: bold; color: #777777; }
span.paren1:hover { color: #777777; background-color: #BAFFFF; }
span.paren2 { color: #777777; }
span.paren2:hover { color: #777777; background-color: #FFCACA; }
span.paren3 { color: #777777; }
span.paren3:hover { color: #777777; background-color: #FFFFBA; }
span.paren4 { color: #777777; }
span.paren4:hover { color: #777777; background-color: #CACAFF; }
span.paren5 { color: #777777; }
span.paren5:hover { color: #777777; background-color: #CAFFCA; }
span.paren6 { color: #777777; }
span.paren6:hover { color: #777777; background-color: #FFBAFF; }

View file

@ -0,0 +1,29 @@
(defsystem "local-time"
:version "1.0.6"
:license "BSD"
:author "Daniel Lowe <dlowe@dlowe.net>"
:description "A library for manipulating dates and times, based on a paper by Erik Naggum"
:depends-on (:cl-fad)
:in-order-to ((test-op (test-op "local-time/test")))
:components ((:module "src"
:serial t
:components ((:file "package")
(:file "local-time")))))
(defsystem "local-time/test"
:version "1.0.6"
:author "Daniel Lowe <dlowe@dlowe.net>"
:description "Testing code for the local-time library"
:depends-on (:stefil
:local-time)
:perform (test-op (o s) (uiop:symbol-call '#:stefil
'#:funcall-test-with-feedback-message
(uiop:find-symbol* '#:test '#:local-time.test)))
:components ((:module "test"
:serial t
:components ((:file "package")
(:file "simple")
(:file "comparison")
(:file "formatting")
(:file "parsing")
(:file "timezone")))))

View file

@ -0,0 +1,46 @@
(in-package :local-time)
(export 'set-local-time-cl-postgres-readers :local-time)
;; Postgresql days are measured from 2000-01-01, whereas local-time
;; uses 2000-03-01. We expect the database server to be in the UTC timezone.
(defconstant +postgres-day-offset-to-local-time+ -60)
(defun set-local-time-cl-postgres-readers (&optional (table cl-postgres:*sql-readtable*))
(flet ((timestamp-reader (usecs)
(multiple-value-bind (days usecs)
(floor usecs +usecs-per-day+)
(multiple-value-bind (secs usecs)
(floor usecs 1000000)
(local-time:make-timestamp :nsec (* usecs 1000)
:sec secs
:day (+ days +postgres-day-offset-to-local-time+))))))
(cl-postgres:set-sql-datetime-readers
:table table
:date (lambda (days)
(local-time:make-timestamp
:nsec 0 :sec 0
:day (+ days +postgres-day-offset-to-local-time+)))
:timestamp #'timestamp-reader
:timestamp-with-timezone #'timestamp-reader
:interval
(lambda (months days usecs)
(declare (ignore months days usecs))
(error "Intervals are not yet supported"))
:time
(lambda (usecs)
(multiple-value-bind (days usecs)
(floor usecs +usecs-per-day+)
(assert (= days 0))
(multiple-value-bind (secs usecs)
(floor usecs 1000000)
(let ((time-of-day (local-time:make-timestamp
:nsec (* usecs 1000)
:sec secs
:day 0)))
(check-type time-of-day time-of-day)
time-of-day)))))))
(defmethod cl-postgres:to-sql-string ((arg local-time:timestamp))
(format nil "'~a'"
(local-time:format-rfc3339-timestring nil arg :timezone +utc-zone+)))

View file

@ -0,0 +1,91 @@
(defpackage #:local-time
(:use #:cl)
(:export #:timestamp
#:date
#:time-of-day
#:make-timestamp
#:clone-timestamp
#:day-of
#:sec-of
#:nsec-of
#:timestamp<
#:timestamp<=
#:timestamp>
#:timestamp>=
#:timestamp=
#:timestamp/=
#:timestamp-maximum
#:timestamp-minimum
#:adjust-timestamp
#:adjust-timestamp!
#:timestamp-whole-year-difference
#:days-in-month
#:timestamp-
#:timestamp+
#:timestamp-difference
#:timestamp-minimize-part
#:timestamp-maximize-part
#:with-decoded-timestamp
#:decode-timestamp
#:timestamp-century
#:timestamp-day
#:timestamp-day-of-week
#:timestamp-decade
#:timestamp-hour
#:timestamp-microsecond
#:timestamp-millennium
#:timestamp-millisecond
#:timestamp-minute
#:timestamp-month
#:timestamp-second
#:timestamp-week
#:timestamp-year
#:parse-timestring
#:format-timestring
#:format-rfc1123-timestring
#:to-rfc1123-timestring
#:format-rfc3339-timestring
#:to-rfc3339-timestring
#:encode-timestamp
#:parse-rfc3339-timestring
#:universal-to-timestamp
#:timestamp-to-universal
#:unix-to-timestamp
#:timestamp-to-unix
#:timestamp-subtimezone
#:define-timezone
#:*default-timezone*
#:*clock*
#:leap-second-adjusted
#:clock-now
#:clock-today
#:find-timezone-by-location-name
#:reread-timezone-repository
#:now
#:today
#:enable-read-macros
#:+utc-zone+
#:+gmt-zone+
#:+month-names+
#:+short-month-names+
#:+day-names+
#:+short-day-names+
#:+seconds-per-day+
#:+seconds-per-hour+
#:+seconds-per-minute+
#:+minutes-per-day+
#:+minutes-per-hour+
#:+hours-per-day+
#:+days-per-week+
#:+months-per-year+
#:+iso-8601-format+
#:+iso-8601-date-format+
#:+iso-8601-time-format+
#:+rfc3339-format+
#:+rfc3339-format/date-only+
#:+asctime-format+
#:+rfc-1123-format+
#:+iso-week-date-format+
#:astronomical-julian-date
#:modified-julian-date
#:astronomical-modified-julian-date))

View file

@ -0,0 +1,128 @@
(in-package #:local-time.test)
(defsuite* (comparison :in simple))
(defmacro defcmptest (comparator-name &body args)
`(deftest ,(symbolicate 'test/simple/comparison/ comparator-name) ()
(flet ((make (day &optional (sec 0) (nsec 0))
(make-timestamp :day day :sec sec :nsec nsec)))
,@(loop
:for entry :in args
:when (= (length entry) 1)
:do (push 'is entry)
:else
:do (if (member (car entry) '(t true is) :test #'eq)
'is
'is)
collect (let ((body `(,comparator-name (make ,@(second entry))
(make ,@(third entry)))))
(cond
((eq (car entry) 'true)
`(is ,body))
((eq (car entry) 'false)
`(is (not ,body)))
(t (error "Don't know how to interpret ~S" entry))))))))
(defcmptest timestamp<
(true (1 0 0)
(2 0 0))
(true (0 1 0)
(0 2 0))
(true (0 0 1)
(0 0 2))
(false (2 0 0)
(1 0 0))
(false (0 2 0)
(0 1 0))
(false (0 0 2)
(0 0 1)))
(defcmptest timestamp<=
(true (1 0 0)
(2 0 0))
(true (0 1 0)
(0 2 0))
(true (0 0 1)
(0 0 2))
(true (1 0 0)
(1 0 0))
(true (1 1 0)
(1 1 0))
(true (1 1 1)
(1 1 1))
(false (2 0 0)
(1 0 0))
(false (0 2 0)
(0 1 0))
(false (0 0 2)
(0 0 1)))
(defcmptest timestamp>
(true (2 0 0)
(1 0 0))
(true (0 2 0)
(0 1 0))
(true (0 0 2)
(0 0 1))
(false (1 0 0)
(2 0 0))
(false (0 1 0)
(0 2 0))
(false (0 0 1)
(0 0 2)))
(defcmptest timestamp>=
(true (2 0 0)
(1 0 0))
(true (0 2 0)
(0 1 0))
(true (0 0 2)
(0 0 1))
(true (1 0 0)
(1 0 0))
(true (1 1 0)
(1 1 0))
(true (1 1 1)
(1 1 1))
(false (1 0 0)
(2 0 0))
(false (0 1 0)
(0 2 0))
(false (0 0 1)
(0 0 2)))
(defcmptest timestamp=
(true (1 0 0)
(1 0 0))
(true (1 1 0)
(1 1 0))
(true (1 1 1)
(1 1 1))
(false (1 0 0)
(2 0 0))
(false (0 1 0)
(0 2 0))
(false (0 0 1)
(0 0 2)))
(deftest test/simple/comparison/timestamp=/2 ()
(is (timestamp= (make-timestamp) (make-timestamp)))
(is (not (timestamp= (make-timestamp) (make-timestamp :nsec 1)))))
(deftest test/simple/comparison/timestamp=/3 ()
(is (eql (handler-case
(timestamp= (make-timestamp) nil)
(type-error ()
:correct-error))
:correct-error)))
(deftest test/simple/comparison/timestamp/= ()
(is (timestamp/= (make-timestamp :nsec 1) (make-timestamp :nsec 2)))
(is (timestamp/= (make-timestamp :nsec 1) (make-timestamp :nsec 2) (make-timestamp :nsec 3)))
(is (not (timestamp/= (make-timestamp :nsec 1) (make-timestamp :nsec 2) (make-timestamp :nsec 1))))
(is (not (timestamp/= (make-timestamp) (make-timestamp)))))

View file

@ -0,0 +1,97 @@
(in-package #:local-time.test)
(defsuite* (formatting :in test))
(deftest test/formatting/format-timestring/1 ()
(let ((*default-timezone* local-time:+utc-zone+)
(test-timestamp (encode-timestamp 1000 2 3 4 5 6 2008 :offset 0)))
(macrolet ((frob (&rest args)
`(progn
,@(loop
:for (a b) :on args :by #'cddr
:collect `(is (string= ,a ,b))))))
(frob
"2008-06-05T04:03:02.000001Z"
(format-timestring nil test-timestamp)
"2008-06-05T04:03:02.000001-05:00"
(let ((utc-5 (local-time::%make-simple-timezone "UTC-5" "UTC-5" -18000)))
(format-timestring nil (encode-timestamp 1000 2 3 4 5 6 2008
:offset (* 3600 -5))
:timezone utc-5))
"Thu Jun 5 04:03:02 2008"
(format-timestring nil test-timestamp :format +asctime-format+)
"Thu, 05 Jun 2008 04:03:02 +0000"
(format-timestring nil test-timestamp :format +rfc-1123-format+)
""
(format-timestring nil test-timestamp
:format nil)
"04"
(format-timestring nil test-timestamp
:format '((:hour 2)))
"04:03"
(format-timestring nil test-timestamp
:format '((:hour 2) #\: (:min 2)))
"5th"
(format-timestring nil test-timestamp
:format '(:ordinal-day))
"2004-W53-6"
(format-timestring nil (encode-timestamp 0 0 0 0 1 1 2005)
:format +iso-week-date-format+)
"2004-W53-7"
(format-timestring nil (encode-timestamp 0 0 0 0 2 1 2005)
:format +iso-week-date-format+)
"2005-W52-6"
(format-timestring nil (encode-timestamp 0 0 0 0 31 12 2005)
:format +iso-week-date-format+)
"2007-W01-1"
(format-timestring nil (encode-timestamp 0 0 0 0 1 1 2007)
:format +iso-week-date-format+)
"2007-W52-7"
(format-timestring nil (encode-timestamp 0 0 0 0 30 12 2007)
:format +iso-week-date-format+)
"2008-W01-1"
(format-timestring nil (encode-timestamp 0 0 0 0 31 12 2007)
:format +iso-week-date-format+)
"2009-W53-5"
(format-timestring nil (encode-timestamp 0 0 0 0 1 1 2010)
:format +iso-week-date-format+)
"2009-W01-3"
(format-timestring nil (encode-timestamp 0 0 0 0 31 12 2008)
:format +iso-week-date-format+)))))
(deftest test/formatting/format-timestring/2 ()
(with-output-to-string (*standard-output*)
(let ((*default-timezone* (find-timezone-by-location-name "UTC")))
(finishes (print (now))))))
(deftest test/formatting/ordinals ()
(flet ((format-ordinal (day)
(format-timestring nil (encode-timestamp 0 0 0 0 day 1 2008)
:format '(:ordinal-day))))
(string= "31st" (format-ordinal 31))
(string= "11th" (format-ordinal 11))
(string= "22nd" (format-ordinal 22))
(string= "3rd" (format-ordinal 3))))
(deftest test/formatting/bug/1 ()
(let ((*default-timezone* (find-timezone-by-location-name "Pacific/Auckland")))
(finishes (format-timestring nil (now)))))
(deftest test/formatting/leap-year ()
(let ((timestamp (parse-timestring "2004-02-29")))
(is (timestamp= timestamp (parse-timestring (format-timestring nil timestamp))))))

View file

@ -0,0 +1,13 @@
(cl:in-package :cl-user)
(defpackage :local-time.test
(:use :alexandria
:common-lisp
:stefil
:local-time))
(in-package :local-time.test)
(defsuite* (test :in root-suite) ()
(local-time::reread-timezone-repository)
(run-child-tests))

View file

@ -0,0 +1,79 @@
(in-package #:local-time.test)
(defsuite* (parsing :in test))
(deftest test/parsing/parse-format-consistency/range (&key (start-day -100000) (end-day 100000))
(declare (optimize debug))
(without-test-progress-printing
(loop
:with time = (make-timestamp)
:for day :from start-day :upto end-day
:for index :upfrom 0 :do
(setf (day-of time) day)
(when (zerop (mod index 10000))
(print time))
(let ((parsed (parse-timestring (format-timestring nil time))))
(is (timestamp= parsed time))))))
(deftest test/parsing/parse-format-consistency ()
(flet ((compare (nsec sec min hour day mon year str
&key start end offset
(allow-missing-elements t))
(let* ((timestamp-a (encode-timestamp nsec sec min hour
day mon year :offset offset))
(used-offset (or offset
(local-time::%guess-offset (sec-of timestamp-a)
(day-of timestamp-a))))
(timestamp-b (parse-timestring str
:start start
:end end
:allow-missing-elements
allow-missing-elements
:offset used-offset)))
(is (timestamp= timestamp-a timestamp-b)))))
(let ((timestamp (now)))
(is (timestamp= timestamp
(parse-timestring
(format-timestring nil timestamp)))))
(let* ((*default-timezone* (find-timezone-by-location-name "Europe/Budapest")))
(compare 0 0 0 0 1 1 1 "0001-01-01T00:00:00,0"))
(compare 0 0 0 0 1 1 1 "0001-01-01T00:00:00Z" :offset 0)
(compare 0 0 0 0 1 1 2006 "2006-01-01T00:00:00,0")
(compare 0 0 0 0 1 1 2006 "xxxx 2006-01-01T00:00:00,0 xxxx"
:start 5
:end 15)
(is (eql (day-of (parse-timestring "2006-06-06TZ")) 2288))
(compare 20000000 3 4 5 6 7 2008 "2008-07-06T05:04:03,02")
(compare 0 2 0 0 23 3 2000 "--23T::02" :allow-missing-elements t)
(compare 80000000 7 6 5 1 3 2000 "T05:06:07,08" :allow-missing-elements t)
(compare 940703000 28 56 16 20 2 2008 "2008-02-20T16:56:28.940703Z"
:offset 0)))
(deftest test/parsing/split ()
(is (equal (local-time::%split-timestring "2006-01-02T03:04:05,6-05")
'(2006 1 2 3 4 5 600000000 -5 0)))
(is (equal (local-time::%split-timestring "2006-01-02T03:04:05,6-0515")
'(2006 1 2 3 4 5 600000000 -5 -15)))
(is (equal (local-time::%split-timestring "2006-01-02T03:04:05,6-05:15")
'(2006 1 2 3 4 5 600000000 -5 -15))))
(deftest test/parsing/reader ()
(let ((now (now)))
(setf (nsec-of now) 123456000)
(is (timestamp= now
(with-input-from-string (ins (format-timestring nil now))
(local-time::%read-timestring ins #\@))))))
(deftest test/parsing/read-universal-time ()
(let ((now (now)))
(setf (nsec-of now) 0)
(is (timestamp= now
(with-input-from-string (ins (princ-to-string (timestamp-to-universal now)))
(local-time::%read-universal-time ins #\@ nil))))))

View file

@ -0,0 +1,284 @@
(in-package #:local-time.test)
(defsuite* (simple :in test))
(eval-when (:compile-toplevel :load-toplevel :execute)
(local-time::define-timezone eastern-tz
(merge-pathnames #p"US/Eastern" local-time::*default-timezone-repository-path*))
(local-time::define-timezone amsterdam-tz
(merge-pathnames #p"Europe/Amsterdam" local-time::*default-timezone-repository-path*)))
(deftest test/simple/make-timestamp ()
(let ((timestamp (make-timestamp :nsec 1 :sec 2 :day 3)))
(is (= (nsec-of timestamp) 1))
(is (= (sec-of timestamp) 2))
(is (= (day-of timestamp) 3))))
(deftest test/simple/read-binary-integer ()
(let ((tmp-file-path #p"/tmp/local-time-test"))
(with-open-file (ouf tmp-file-path
:direction :output
:element-type 'unsigned-byte
:if-exists :supersede)
(dotimes (i 14)
(write-byte 200 ouf)))
(with-open-file (inf tmp-file-path :element-type 'unsigned-byte)
(is (eql (local-time::%read-binary-integer inf 1) 200))
(is (eql (local-time::%read-binary-integer inf 1 t) -56))
(is (eql (local-time::%read-binary-integer inf 2) 51400))
(is (eql (local-time::%read-binary-integer inf 2 t) -14136))
(is (eql (local-time::%read-binary-integer inf 4) 3368601800))
(is (eql (local-time::%read-binary-integer inf 4 t) -926365496)))))
(deftest test/simple/encode-timestamp ()
(macrolet ((entry ((&rest encode-timestamp-args)
day sec nsec)
`(let ((timestamp (encode-timestamp ,@encode-timestamp-args)))
(is (= (day-of timestamp) ,day))
(is (= (sec-of timestamp) ,sec))
(is (= (nsec-of timestamp) ,nsec)))))
(entry (0 0 0 0 1 3 2000 :offset 0)
0 0 0)
(entry (0 0 0 0 29 2 2000 :offset 0)
-1 0 0)
(entry (0 0 0 0 2 3 2000 :offset 0)
1 0 0)
(entry (0 0 0 0 1 1 2000 :offset 0)
-60 0 0)
(entry (0 0 0 0 1 3 2001 :offset 0)
365 0 0)))
(defmacro encode-decode-test (args &body body)
`(let ((timestamp (encode-timestamp ,@(subseq args 0 7) :offset 0)))
(is (equal '(,@args ,@(let ((stars nil))
(dotimes (n (- 7 (length args)))
(push '* stars))
stars))
(multiple-value-list
(decode-timestamp timestamp :timezone local-time:+utc-zone+))))
,@body))
(deftest test/simple/encode-decode-consistency/1 ()
(encode-decode-test (5 5 5 5 5 5 1990 6 nil 0 "UTC"))
(encode-decode-test (0 0 0 0 1 3 2001 4 nil 0 "UTC"))
(encode-decode-test (0 0 0 0 1 3 1998 0 nil 0 "UTC"))
(encode-decode-test (1 2 3 4 5 6 2008 4 nil 0 "UTC"))
(encode-decode-test (0 0 0 0 1 1 1 1 nil 0 "UTC")))
(deftest test/simple/encode-decode-consistency/random ()
(loop :repeat 1000 :do
(let ((timestamp (make-timestamp :day (- (random 65535) 36767)
:sec (random 86400)
:nsec (random 1000000000))))
(multiple-value-bind (ns ss mm hh day month year)
(decode-timestamp timestamp :timezone local-time:+utc-zone+)
(is (timestamp= timestamp
(encode-timestamp ns ss mm hh day month year :offset 0)))))))
;;;;;;
;;; TODO the rest is uncategorized, just simply converted from the old 5am suite
(deftest test/timestamp-conversions ()
(is (eql 0 (timestamp-to-unix
(encode-timestamp 0 0 0 0 1 1 1970 :offset 0))))
(is (equal (values 2 3 4 5 6 2008 3 * *)
(decode-universal-time
(timestamp-to-universal
(encode-timestamp 1 2 3 4 5 6 2008 :offset 0)) 0)))
(let ((now (now)))
(setf (nsec-of now) 0)
(is (timestamp= now
(unix-to-timestamp (timestamp-to-unix now)))))
(let ((now (get-universal-time)))
(is (equal now
(timestamp-to-universal (universal-to-timestamp now))))))
(deftest test/year-difference ()
(let ((a (parse-timestring "2006-01-01T00:00:00"))
(b (parse-timestring "2001-01-01T00:00:00")))
(is (= 5 (timestamp-whole-year-difference a b))))
(let ((a (parse-timestring "2006-01-01T00:00:00"))
(b (parse-timestring "2001-01-02T00:00:00")))
(is (= 4 (timestamp-whole-year-difference a b))))
(let* ((local-time::*default-timezone* amsterdam-tz)
(a (parse-timestring "1978-10-01")))
(is (= 0 (timestamp-whole-year-difference a a)))))
(deftest test/adjust-timestamp/day-of-week/basic ()
(let ((sunday (parse-timestring "2020-09-13T00:00:00"))
(saturday (parse-timestring "2020-09-19T00:00:00"))
(base (parse-timestring "2020-09-15T00:00:00")))
(loop for dow in '(:sunday :monday :tuesday)
do (let ((modified (adjust-timestamp base (timezone +utc-zone+) (offset :day-of-week dow))))
(is (and (timestamp>= modified sunday) (timestamp<= modified base)))))
(loop for dow in '(:wednesday :thursday :friday :saturday)
do (let ((modified (adjust-timestamp base (timezone +utc-zone+) (offset :day-of-week dow))))
(is (and (timestamp> modified base) (timestamp<= modified saturday)))))))
(deftest test/adjust-timestamp/bug1 ()
(let* ((timestamp (parse-timestring "2006-01-01T00:00:00Z"))
(modified-timestamp (adjust-timestamp timestamp (timezone +utc-zone+) (offset :year 1))))
(is (timestamp= (parse-timestring "2007-01-01T00:00:00Z") modified-timestamp))))
(deftest test/adjust-timestamp/bug2 ()
(let* ((timestamp (parse-timestring "2009-03-01T01:00:00.000000+00:00"))
(modified-timestamp (adjust-timestamp timestamp (timezone +utc-zone+) (offset :month 1))))
(is (timestamp= (parse-timestring "2009-04-01T01:00:00.000000+00:00") modified-timestamp))))
(deftest test/adjust-timestamp/bug3 ()
(let* ((timestamp (parse-timestring "2009-03-01T01:00:00.000000+00:00"))
(modified-timestamp (adjust-timestamp timestamp (timezone +utc-zone+) (offset :day-of-week :monday))))
(is (timestamp= (parse-timestring "2009-02-23T01:00:00.000000+00:00") modified-timestamp)))
(let* ((timestamp (parse-timestring "2009-03-04T01:00:00.000000+00:00"))
(modified-timestamp (adjust-timestamp timestamp (timezone +utc-zone+) (offset :day-of-week :monday))))
(is (timestamp= (parse-timestring "2009-03-02T01:00:00.000000+00:00") modified-timestamp))))
(deftest test/adjust-timestamp/bug4 ()
(let* ((timestamp (parse-timestring "2013-04-30T00:00:00.000000+00:00"))
(modified-timestamp (adjust-timestamp timestamp (timezone +utc-zone+) (offset :day-of-week :wednesday))))
(is (timestamp= (parse-timestring "2013-05-01T00:00:00.000000+00:00") modified-timestamp)))
(let* ((timestamp (parse-timestring "2013-12-31T00:00:00.000000+00:00"))
(modified-timestamp (adjust-timestamp timestamp (timezone +utc-zone+) (offset :day-of-week :wednesday))))
(is (timestamp= (parse-timestring "2014-01-01T00:00:00.000000+00:00") modified-timestamp))))
#+nil
(deftest test/adjust-days ()
(let ((sunday (parse-timestring "2006-12-17T01:02:03Z")))
(is (timestamp= (parse-timestring "2006-12-11T01:02:03Z")
(adjust-timestamp sunday (offset :day-of-week :monday))))
(is (timestamp= (parse-timestring "2006-12-20T01:02:03Z")
(adjust-timestamp sunday (offset :day 3))))))
(deftest test/decode-date ()
(loop
:for (total-day year month day) :in '((-1 2000 02 29)
(0 2000 03 01)
(1 2000 03 02)
(364 2001 02 28)
(365 2001 03 01)
(366 2001 03 02)
(#.(* 2 365) 2002 03 01)
(#.(* 4 365) 2004 02 29)
(#.(1+ (* 4 365)) 2004 03 01))
:do (multiple-value-bind (year* month* day*)
(local-time::%timestamp-decode-date total-day)
(is (= year year*))
(is (= month month*))
(is (= day day*)))))
(deftest test/timestamp-decoding-readers ()
(let ((*default-timezone* +utc-zone+))
(dolist (year '(1900 1975 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010))
(dolist (month '(1 2 3 4 5 6 7 8 9 10 11 12))
(dolist (day '(1 2 3 27 28 29 30 31))
(when (valid-date-tuple? year month day)
(let ((hour (random 24))
(min (random 60))
(sec (random 60))
(nsec (random 1000000000)))
(let ((time (encode-timestamp nsec sec min hour day month year :offset 0)))
(is (= (floor year 10) (timestamp-decade time)))
(is (= year (timestamp-year time)))
(is (= month (timestamp-month time)))
(is (= day (timestamp-day time)))
(is (= hour (timestamp-hour time)))
(is (= min (timestamp-minute time)))
(is (= sec (timestamp-second time)))
(is (= (floor nsec 1000000)
(timestamp-millisecond time)))
(is (= (floor nsec 1000)
(timestamp-microsecond time)))))))))))
(deftest test/timestamp-century ()
(let ((*default-timezone* +utc-zone+))
(dolist (year-data '((-101 -2)
(-100 -1)
(-1 -1)
(1 1)
(100 1)
(101 2)
(1999 20)
(2000 20)
(2001 21)))
(let ((time (encode-timestamp 0 0 0 0 1 1 (first year-data) :offset 0)))
(is (= (second year-data) (timestamp-century time)))))))
(deftest test/timestamp-millennium ()
(let ((*default-timezone* +utc-zone+))
(dolist (year-data '((-101 -1)
(-100 -1)
(-1 -1)
(1 1)
(100 1)
(101 1)
(1001 2)
(1999 2)
(2000 2)
(2001 3)))
(let ((time (encode-timestamp 0 0 0 0 1 1 (first year-data) :offset 0)))
(is (= (second year-data) (timestamp-millennium time)))))))
(defun valid-date-tuple? (year month day)
;; it works only on the gregorian calendar
(let ((month-days #(31 28 31 30 31 30 31 31 30 31 30 31)))
(and (<= 1 month 12)
(<= 1 day (+ (aref month-days (1- month))
(if (and (= month 2)
(zerop (mod year 4))
(not (zerop (mod year 100)))
(zerop (mod year 400)))
1
0))))))
(deftest test/encode-decode-timestamp ()
(let ((*default-timezone* +utc-zone+))
(loop for year :in '(1900 1975 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010) do
(loop for month :from 1 :to 12 do
(loop for day :in '(1 2 3 27 28 29 30 31) do
(when (valid-date-tuple? year month day)
(multiple-value-bind (nsec sec minute hour day* month* year* day-of-week)
(decode-timestamp (encode-timestamp 0 0 0 0 day month year :offset 0))
(declare (ignore nsec sec minute day-of-week))
(is (= hour 0))
(is (= year year*))
(is (= month month*))
(is (= day day*)))))))))
(deftest test/timestamp-maximize-part ()
(timestamp= (timestamp-maximize-part
(encode-timestamp 0 49 26 13 9 12 2010 :offset -18000)
:min)
(encode-timestamp 999999999 59 59 13 9 12 2010 :offset -18000)))
(deftest test/timestamp-minimize-part ()
(timestamp= (timestamp-minimize-part
(encode-timestamp 0 49 26 13 9 12 2010 :offset -18000)
:min)
(encode-timestamp 0 0 0 13 9 12 2010 :offset -18000)))
(deftest test/decode-iso-week ()
(dolist (*default-timezone* (list eastern-tz +utc-zone+ amsterdam-tz))
(dolist (testcase '((2005 01 01 2004 53 6)
(2005 01 02 2004 53 7)
(2005 12 31 2005 52 6)
(2007 01 01 2007 1 1)
(2007 12 30 2007 52 7)
(2007 12 31 2008 1 1)
(2008 01 01 2008 1 2)
(2008 12 28 2008 52 7)
(2008 12 29 2009 1 1)
(2008 12 30 2009 1 2)
(2008 12 31 2009 1 3)
(2009 01 01 2009 1 4)
(2009 12 31 2009 53 4)
(2010 01 01 2009 53 5)
(2010 01 02 2009 53 6)
(2010 01 03 2009 53 7)
(2016 01 04 2016 1 1)))
(destructuring-bind (year month day iso-year iso-week iso-dow)
testcase
(let ((ts (encode-timestamp 0 0 0 12 day month year)))
(is (equal (list iso-year iso-week iso-dow)
(multiple-value-list (local-time::%timestamp-decode-iso-week ts)))))))))

View file

@ -0,0 +1,106 @@
(in-package #:local-time.test)
(defsuite* (timezone :in test))
(eval-when (:compile-toplevel :load-toplevel :execute)
(local-time::define-timezone eastern-tz
(merge-pathnames #p"EST5EDT" local-time::*default-timezone-repository-path*))
(local-time::define-timezone utc-leaps
(merge-pathnames #p"../zoneinfo-leaps/UTC" local-time::*default-timezone-repository-path*)))
(deftest transition-position/correct-position ()
(let ((cases '((0 #(1 2 3 4 5) 0)
(1 #(1 2 3 4 5) 0)
(2 #(1 2 3 4 5) 1)
(3 #(1 2 3 4 5) 2)
(4 #(1 2 3 4 5) 3)
(5 #(1 2 3 4 5) 4)
(1 #(1 3 5) 0)
(2 #(1 3 5) 0)
(3 #(1 3 5) 1)
(4 #(1 3 5) 1)
(5 #(1 3 5) 2)
(6 #(1 3 5) 2)
(1 #(1 3 5 7) 0)
(2 #(1 3 5 7) 0)
(3 #(1 3 5 7) 1)
(4 #(1 3 5 7) 1)
(5 #(1 3 5 7) 2)
(6 #(1 3 5 7) 2)
(7 #(1 3 5 7) 3)
(8 #(1 3 5 7) 3)
)))
(dolist (case cases)
(destructuring-bind (needle haystack want)
case
(let ((got (local-time::transition-position needle haystack)))
(is (= got want)
"(transition-position ~a ~a) got ~a, want ~a"
needle haystack got want))))))
(deftest test/timezone/decode-timestamp-dst ()
;; Testing DST calculation with a known timezone
(let ((test-cases '(
;; Spring forward
((2008 3 9 6 58) (2008 3 9 1 58))
((2008 3 9 6 59) (2008 3 9 1 59))
((2008 3 9 7 0) (2008 3 9 3 0))
((2008 3 9 7 1) (2008 3 9 3 1))
;; Fall back
((2008 11 2 5 59) (2008 11 2 1 59))
((2008 11 2 6 0) (2008 11 2 1 0))
((2008 11 2 6 1) (2008 11 2 1 1)))))
(dolist (test-case test-cases)
(is (equal
(let ((timestamp
(apply 'local-time:encode-timestamp
`(0 0 ,@(reverse (first test-case)) :offset 0))))
(local-time:decode-timestamp timestamp :timezone eastern-tz))
(apply 'values `(0 0 ,@(reverse (second test-case)))))))))
(deftest test/timezone/adjust-across-dst-by-days ()
(let* ((old (parse-timestring "2014-03-09T01:00:00.000000-05:00"))
(new (timestamp+ old 1 :day eastern-tz)))
(is (= (* 23 60 60) (timestamp-difference new old)))))
(deftest test/timezone/adjust-across-dst-by-hours ()
(let* ((old (parse-timestring "2014-03-09T01:00:00.000000-05:00"))
(new (timestamp+ old 24 :hour eastern-tz)))
(is (= (* 24 60 60) (timestamp-difference new old)))))
(deftest test/timezone/timestamp-minimize-part ()
(is (timestamp=
(timestamp-minimize-part
(encode-timestamp 999999999 59 59 1 14 3 2010 :timezone eastern-tz)
:month
:timezone eastern-tz)
(encode-timestamp 0 0 0 0 1 1 2010 :timezone eastern-tz)))
(is (timestamp=
(timestamp-minimize-part
(encode-timestamp 0 0 0 2 14 3 2010 :timezone eastern-tz)
:month
:timezone eastern-tz)
(encode-timestamp 0 0 0 0 1 1 2010 :timezone eastern-tz))))
(deftest test/timezone/timestamp-maximize-part ()
(is (timestamp=
(timestamp-maximize-part
(encode-timestamp 999999999 59 59 1 7 11 2010 :timezone eastern-tz)
:month
:timezone eastern-tz)
(encode-timestamp 999999999 59 59 23 31 12 2010 :timezone eastern-tz)))
(is (timestamp=
(timestamp-maximize-part
(encode-timestamp 0 0 0 2 7 11 2010 :timezone eastern-tz)
:month
:timezone eastern-tz)
(encode-timestamp 999999999 59 59 23 31 12 2010 :timezone eastern-tz))))
(deftest test/leaps/tai-to-utc ()
(let ((*default-timezone* utc-leaps))
(is (= 1435708799
(local-time::%adjust-sec-for-leap-seconds 1435708824)
(local-time::%adjust-sec-for-leap-seconds 1435708825)))
(is (= 1435708800
(local-time::%adjust-sec-for-leap-seconds 1435708826)))))

Some files were not shown because too many files have changed in this diff Show more