low-level date modules

2006-06-15 Thread Zefram
I've written a couple of Perl modules that do low-level date stuff,
way below the high-level DateTime concept.  I'd appreciate a review
of my modules by the date experts on this list.  The modules are
Date::JD, which does conversions between several flavours of Julian
Date, and Date::ISO8601, which implements the ISO 8601 calendrs in
terms of Chronological Julian Day Numbers.  They can be found at
<http://search.cpan.org/~zefram/>.  Thanks.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Joshua Hoblitt wrote:
>Is all that stuff just a reinvention of the wheel?

Yes, but this time it's round.

>What's the value add
>over DateTime and the zillions of other date/time modules already on CPAN?

Three things: API isolation; arithmetic-friendly (and calendar-neutral)
interface; unlimited range.

The Date::ISO8601 module implements those calendars *in isolation*.
It's suitable to plug into anything that wants to use those calendars
as part of something else.  Modules such as DateTime implement these
calendars, but that implementation comes along with mandatory processing
of time of day and timezone.  DateTime offers no object that represents
merely an abstract date.

Secondly, most calendar-handling modules offer functions that, for
example, take a (year, month, day) tuple and a number of days and
return a (year, month, day) tuple that identifies the date so many
days later.  This way of doing date arithmetic is the wrong way round.
Date::ISO8601 treats the (year, month, day) tuple as merely a surface
encoding of the date, which is what it is, and not as the natural form of
the underlying data.  Using CJDN, a linear count of days, as the basic
format for representing dates lets the user do the arithmetic directly.
It also avoids forcing the user to deal with one calendar and its (year,
month, day) tuples when ey might only be interested in a different
calendar, such as the (year, week, day) calendar from ISO 8601.

Finally, my modules allow use of bignums, avoiding range limitations.
Several date-handling modules on CPAN actually impose arbitrary limits
on the dates they will handle.  The ones that don't, apart from mine,
break when the dates overflow the native integer format.

Additionally, for Date::JD I've never seen a CPAN module with that
functionality at all.

>I have to admit I haven't really looked at your code in depth but I
>don't see how you can accurately be calculating TT/etc. without doing an
>interpolation of IERS data.

Er, you can't be talking about Date::ISO8601 and Date::JD here.
I guess you're talking about my Time::TT and Time::TAI?  These do indeed
convert between different realisations of TT by interpolating BIPM data.
Quite a different subject area from what (it seems) this mailing list
is concerned with, but a good example of why one might want to use more
than one model of time-of-day with a calendar module.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Joshua Hoblitt wrote:
>Oops - I made a typo.  Replace TT with UT1.

I haven't written any code that tackles UT1 yet.  My interests lie
firmly in atomic (and platonic) timekeeping.  Watching a planet rotate
is too much like getting my hands dirty.  Though I did write Time::UTC,
because I wanted to handle civil time and there was nothing on CPAN that
handled leap seconds correctly.

By the way, thanks everyone for writing DateTime::TimeZone.  I'm glad
I didn't have to write *that* one myself.  (I fucking hate timezones,
but I do want my desktop clock program to display regional civil time
rather than unadulterated UTC.)

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Eugene van der Pijll wrote:
>is any instance where DateTime gets it wrong, please let us know.

There's leap seconds, but I presume that's intentional.  AFAIK DateTime
operates correctly within its designed limits.

>priorities of DT have always been 1) correctness 2) ease of use 3)
>efficiency, in that order. I think.

That's refreshing to hear.  I have the same priority list.

>Last weekend I finally did something about it. I've created a new
>version of DateTime, where I have split the module in DT::Date and
>DT::Time.

Cool.  I think there's a lot of abstraction inversion in DateTime,
and this sort of rearrangement is the right direction to rectify that.
It would be nice to mix and match date and time-of-day models.

>I think DateTime::Format::Epoch contains most of the functionality of
>your Date::JD.

I think there'll be resolution issues, and possibly time-of-day model
problems.  The biggest problem is the timezone stuff, though.  And, as I
said, DateTime has no concept matching what a CJDN (or a JDN) represents.

>Those modules are certainly on-topic on this mailing list,

Ah, I was mistaken then.  I'd be interested in suggestions people can
make about those too.  I've tried to make them all interoperate nicely.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Joshua Hoblitt wrote:
>Entirely accidental, I assure you!

Oh dear.

There are two fundamental problems with DateTime's handling of leap
seconds: one pertaining to the future, and one pertaining to the past.

For future times, DateTime implicitly assumes that there will never be
another leap second.  This assumption is incorrect.  It is fundamental
to the definition of UTC that more leap seconds will be announced
from time to time.  It is impossible to have a complete list of leap
seconds.  DateTime's assumption that its leap second list is complete
is so fundamentally incorrect that I took it to be a deliberate design
decision.  Unknown leap seconds are quite a complication in dealing with
future dates: ignoring them is a possible way to cope, but not one that
I recommend.

For past times, DateTime has a time-of-day model where the same time
scale which contains leap seconds extends infinitely far backwards with
no leaps before 1972.  UTC actually had leaps (but not of whole seconds),
and a variable rate, prior to 1972, going back to 1961.  Prior to 1961
there is no UTC.  An accurate representation of UTC can't do anything
with times earlier than 1961, or 1972 if its scope is limited to the
leap second version.  So it's not clear what time scale you intend to
use prior to 1972.  Once again I took it to be a design decision to
fudge the issue and invent a time scale of convenience.

>But you know this as you've had to implement leapseconds in
>Time::UTC too.

Yes.  I grok UTC.

I'm also on the mailing list discussing the future of leap seconds, btw.
Debate has raged there for several years with no conclusion in sight.
Recently there's been some ontological discussion that might form a basis
on which to frame the debate properly.  I think we'll need some similar
work on ontology here if DateTime is to have a more sophisticated concept
of time-of-day.

>Uh, what about the ->jd() & ->mjd() DateTime.pm methods?

Mmm.  There's a trivial issue that they're subject to floating
point rounding which limits the resolution to about 80 us.  And a
not-quite-so-trivial issue that the structure of the floating point
operations is liable to introduce errors from multiple rounding.
The biggie, though, is the handling of leap seconds.  JDs don't really
work properly with UTC, of course, so having a ->jd method without further
explanation, when you have a UTCish time-of-day model, seems unwise.

There is no tradition (AFAIK) of calculating the fractional JD the
way you do, with the JD incrementing at a different rate for the whole
leap-second-containing day.  The official formulae that use JD (usually
MJD actually) with UTC have it increase always at the standard rate of
1/86400 per second.  In that model it undergoes a discontinuity at the end
of a leap, decrementing by 1/86400 at the end of a positive leap second.
A bit like what the POSIX time_t is specified to do (not what it usually
does in practice).  See <http://maia.usno.navy.mil/ser7/tai-utc.dat>
which uses this model of MJD.

The other way to derive a JD from UTC is to use UTC-SLS
<http://www.cl.cam.ac.uk/~mgk25/time/utc-sls/>, which changes rate for
the last 1000 s of a UTC day.  I wrote a module which implements this.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Eugene van der Pijll wrote:
> It's enough for my needs anyway.

Dangerous words.  There certainly are users that need greater precision.
I recommend Math::BigRat, which you'll have noticed I'm quite a fan of.
Bignums mean never having to think how big your floats are.

>I don't understand why you say DT does not know about CJDNs.

Did you notice that JD refers to Universal Time but CJD is
timezone-relative?  That's one of the things that Date::JD makes work
right.  In DateTime this issue gets into the floating timezone, which
looks like a right can of worms.  I think the floating timezone is trying
to make DateTime do too many things in one class.  It's schizophrenic.

>DT is built around the very similar concept of RDN, rata die.

I'm familiar with RD.  I found the book too muddled for my liking.
Interesting stuff if one can think past the details, though.

But the interface to the DateTime class seems to be more concerned
with the Gregorian calendar than RD.  I don't see a way to construct
a DateTime from RD there, just the Gregorian (year, month, day) tuple.
Looks like another abstraction inversion to me: what's a specific calendar
doing in such a fundamental class?

>of DT::Calendars is always done by transforming to RD's and back,

How do you convert between two non-Gregorian calendars using DateTime?
It looks to me like the way the calendar modules subclass DateTime
mean that you can only deal with one non-Gregorian calendar at once.
Am I misunderstanding it?

> my hobby is genealogy, and I'm already happy if I
>get the year right. ;-)

Heh.  That's tricky enough, for the Middle Ages, with so many competing
dates to start the year.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Eugene van der Pijll wrote:
>For every future leap second, there will be a version of DT that
>handles it correctly. That the current implementation can not, is only
>an implentation problem :-).

Having the implementation thus permanently buggy seems like a bad idea.
Also, the version of DT that someone is using might not even know about
leap seconds that have already happened.  You'll get different answers
from different versions, not just at different times.

>Seriously, for problems like "2030-06-30T23:59:59Z + 1 second = ",
>you need to make an assumption about future leap seconds.

I think the only correct approach is to assume nothing and answer "I
don't know".

>   That is a
>decision that may not be appreciated by the users of our modules that
>are not as familiar with leap seconds as you.

I think to do date arithmetic around possible future leap seconds you
need to either represent the uncertainty or have the user say what
assumptions to make.  It would be nice to be able to represent the time
of a hypothetical future leap second, if I want to guess about when they
might be and schedule events for them.  Generally I think this falls
under the rubric of giving DT a more sophisticated model of time-of-day.

> We should not perhaps be calling our time scale
>"utc"; but that would again be hard to explain to ordinary users.

How times change.  When the POSIX time_t stuff was being written, the
knowledgeable people on the committee who wanted to write in terms of
"UTC" were (for a while) overruled by those who didn't want to confuse
readers with unfamiliar acronyms.  They wanted to stick with their nice,
familiar (and wrong) "GMT".

I think you should at least document how your time scale goes.  It'd be
better to give DT knowledge of multiple time scales.

>At the *end* of a leap second? That seems counterintuitive, but if you
>say so... This would mean that the jd value of time points during the
>leap seconds appear to belong to the next day.

Yes, it works that way.  See, if I represent a UTC time as a tuple of
(mjdn, ssm), where ssm is "seconds since midnight", the calculation goes

MJD[UTC] = mjdn + ssm/86400

The discontinuity is at the end of the day.  Also think about a negative
leap: the discontinuity is there also at the end of the day, only 86399
s since midnight.  We don't wait until 86400 s since midnight, 1 s after
midnight of the next day, to jump the MJD.  There actually were negative
leaps in the rubber seconds era.  Btw, in the rubber seconds era the
ssm used in this calculation is UTC seconds, not TAI seconds.

>By the way, it seems that my DT::F::E::JD module gets this right.

Since, as I said, it's a slightly ill-defined concept anyway, "right"
is not so much any particular behaviour, but rather documenting exactly
what you do.  Anything that mixes JD and UTC really needs to document
how it maps between them.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Rick Measham wrote:
>I'm still trying to convince people that you can't just add 24*60*60 to 
>time() to add a day ..

That's one of the things you *can* do, unless you're starting in a leap
second (in which case time() is ambiguous).  time() increases exactly
86400 per UTC day, regardless of day length.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Eugene van der Pijll wrote:
>But have you looked at the DT::Format::Epoch example in my first mail yet?

Your example of usage made it clearer.  I'm not sure what to make of
it yet.  I need to ponder it some more.

There's a basic difference in our approaches to JDs here.  You've
implemented CJD as a calendar, a way to denote points in time that are
abstractly represented by DateTime objects.  That's a good thing to
do, once you've got DTs.  What I did with Date::JD was at an entirely
different level: it has no canonical date format at all, and converts
between JD flavours as peers.  Yours makes more conceptual sense for users
of calendar-like modules; Date::JD is intended purely as glue *between*
calendar modules.  I'm not sure a comparison is appropriate.

Anyway, glad to see that DT::F::Epoch can do that.  I didn't realise it
was that flexible.

>If you care about leap seconds, just stay far away from the floating
>timezone.

I noticed that bit.

>The only reason DateTime.pm (the module) seems to be the fundamental
>class of DateTime (the project) is the name; it has no central role,
>although there are a number of modules (e.g. formatting modules) that
>have been written especially for DT.pm, because it is the most commonly
>used.

Curious.  Is there actually any class with a central role of representing
dates/times?

>Every DT::Calendar module has two required functions: one that returns
>the RD and the number of elapsed seconds in the day; and one that
>accepts these values and converts them to a new object.

What does the object actually represent?

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Rick Measham wrote:
> how can you be 
>happy with adding 86400 seconds for a day (even in UTC) when leapseconds 
>make it ambiguous?

Oh, I'm not happy with it.  time_t is not suitable for serious
timekeeping.  It's just that if one *is* using time_t then that's how
it works.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Eugene van der Pijll wrote:
>Importantly, there are no "calendar-less" datetime objects. One may see
>this as an important difference between your approach and DateTime's;
>one may also see it as an implementation detail.

I think it's a very important architectural issue.  To my mind a date
(or date+time) exists quite independently of calendars.  Each calendar is
merely a way of representing these dates.  The DateTime way makes it look
like the expression of the date is part of its identity.  Of course, if
one has the right mental model, that it is the RDN that directly encodes
the abstract date identity, then it becomes clear that what the DateTime
(or DateTime::Calendar::Whatever) represents is not really the date but
some calendar-specific aspects of date representation.

Something you left out of your description: the date data in a DateTime
can be modified.  Here once again the object is not representing a
date per se.  If I consider the date that in ISO 8601 is represented
as "2006-07-18", it is meaningless to say that I "change its month
to August".  That date is irrevocably in the Gregorian month July.
Yet DateTime lets me do

$dt = DateTime->new(year => 2006, month => 7, day => 18);
$dt->set_month(8);
print "$dt\n";

=> 2006-08-18T00:00:00

Of course I haven't changed 2006-07-18; all I've changed is the content
of a modifiable part of the DateTime object.  So a DateTime actually
represents a *variable*, typed to hold a date (and time), viewed from
a distinctly Gregorian viewpoint.

My feeling on variables is that Perl already has a pretty good
implementation of them, and so building this facility into the DateTime
class is reinventing the wheel.  And this one looks square: it doesn't
seem to be possible to modify the entire content of the variable in
one go except by a ->set call with eight parameters.  I don't want
to single out DateTime for criticism on this point, though.  A lot of
classes all over the place do the same thing.  Even Math::BigRat, which
I curse every time I manage to accidentally modify one.

Anyway, having rambled a bit there, the point was that this is another of
the bits of baggage that comes with DateTime that I'd rather do without.
Which is why I started writing much plainer date-handling modules.

And a bonus point which falls out of this thread of discussion: I like
very clear documentation, and particularly a clear concept of what an
object represents.  So I think it'd be good if we could come up with
sentences starting "An object of this type represents ..." to go at the
top of each class's documentation.

-zefram


Re: low-level date modules

2006-07-17 Thread Zefram
Joshua Hoblitt wrote:
>How is it buggy?

In the same sense that it'll be fixed by a future version's knowledge
of each future leap second.  You not-seriously suggested that the
incompleteness of the leap second list was merely a bug that would be
fixed in a future version.

In reality it's not a bug for the list to be incomplete.  It's a bug
for it to treat the list as complete.

>So what are you proposing?  Modeling future leapseconds?

There are so many ways to model them.  Over the long term (centuries)
it's possible to forecast approximately how many leap seconds will have
occurred by then.  Stating actual dates for them is right out, though.
There are purposes for which this kind of estimate is useful.  But I
think it's a pretty specialised requirement.

>I would really like to hear your solution to this problem.

I don't have a solution, sorry.  Only some vague thoughts.  I think
DateTime should probably provide more than one way to handle unknown
leap seconds.  The user ought to be able to say "do this calculation
assuming an average of one leap second every 500 days over this period".
The user also ought to be able to say "I need an exact answer, so complain
if the answer depends on leap seconds that you don't know about".
I think on correctness grounds the default behaviour should not make
any assumptions at all about unknown leap seconds.  And I think it all
needs to be documented, whatever the behaviour is.

-zefram


Re: low-level date modules

2006-07-18 Thread Zefram
Dave Rolsky wrote:
>>Which is why I started writing much plainer date-handling modules.
>
>Modules which while interesting are unlikely to be comprehensible to most 
>folks.

Are they really *incomprehensible* to most people?  But I didn't aim
them at "most people" as you describe them.  I wrote the modules for
programmers who know what they're doing or are willing to learn.

>As an aside, I've gotta say I'm really disappointed that you went to the 
>trouble of writing all this code _before_ talking to anyone on this list. 

Sorry about that.  I was initially under the impression that this mailing
list was strictly concerned with DateTime and its existing architecture,
rather than with date/time issues in general.  I got pointed here from
the modules mailing list, when I tried to register Date::JD.

-zefram


Re: month_name() returns UTF-8 or Latin-1?

2006-08-27 Thread Zefram
Dave Rolsky wrote:
> 97-111-251-116

That's not a well-formed UTF-8 sequence.  It *is* correct Latin-1
or Unicode, encoding a sequence of four characters.

> IS UTF8

That refers to the internal UTF-8 flag on the string.  That means it's
internally represented using UTF-8, which usually means that the string
being represented is Unicode, not UTF-8.

-zefram


Re: simple calculation frustrations

2006-11-02 Thread Zefram
Matthew wrote:
>But! Then the time changed and now the calculations are off by 1 hour.

Not at all!  The calculations will continue to give you precise one-week
intervals.  It's just that with the change in timezone offset these
intervals are no longer *described* using the same time of day.

OK, so you want the other behaviour.  I suggest that instead of storing
an actual timestamp you should store a broken-down time: week number,
day of week, local time of day, and (most important) timezone rule.
You can put those together in DateTime to find out what point in time
they refer to.  Increment the week number as required.

Watch out for times of day that get repeated or skipped at offset changes.
These are different times of day in different timezones.

-zefram


Re: simple calculation frustrations

2006-11-03 Thread Zefram
Dave Rolsky wrote:
>Pretty much all DBMS's (ok, not SQLite) have support for datetime as a 
>data type.

Worms.  Can thereof.  A datetime type is not a primary feature of a DBMS,
so it's probably not had much attention paid to it.  Until recently,
MySQL's datetime type allowed dates such as 2006-04-31.  Even if it's
implemented correctly, it'll implement some arbitrarily selected date
model that's not particularly likely to match your need.  In this case,
timezone behaviour is the issue, and not very many datetime types handle
multiple timezones in the way desired.

Using a DBMS's datetime type also loses you portability, of course.

>The advantage to this is that your datetime data is stored in a consistent 
>way, so if you ever need to compare datetimes for some reason, you know 
>that they'll compare sanely.

I think what he wants to store isn't actually a date, as in a point in
time.  Looks like he wants to store a time of week, timezone-relative,
along with a timezone spec.  The incrementing loop he came in with is
just answering the question "when will that time of week next occur?".

-zefram


Re: is YYYY-MM-DD HH:MM:SS an ISO8601 format?

2006-11-15 Thread Zefram
Joshua Hoblitt wrote:
>Thoughts or comments on this bug that was just filed on DT::F::ISO8601
>would be appreciated.  The summary: "-MM-DD HH:MM:SS" is not accept
>by DT::F::ISO8601 and I don't believe it's a valid 8601 format.

Your analysis is correct.  Default ISO 8601 rules require date
and time-of-day portions to be separated by a "T" or "t".  So
"2006-11-15T11:04:40" and "20061115t110440" are valid.  Only by prior
agreement may the separator be omitted.

The "prior agreement" expression means that it's not standard, and
formally has the status of any other non-standard variation that one
might invent.  Leaving out the separator is technically as non-conforming
as putting the time-of-day portion first.  However, the standard committee
is suggesting that omitting the separator is a particularly reasonable
variation.  When the need (a tight space constraint) exists, this is
the approved way to adapt the rules.  The same expression is used for
variations such as five-digit years, to address other situations that
the basic standard doesn't cover.

Omitting the separator is presumably meant to allow the format
"20061115110440", not "2006-11-1511:04:40".  The latter would be silly,
saving a character in one place at the expense of conformance while
retaining other unnecessary separators.

The common practice of replacing the "T" with a space isn't mentioned in
the standard at all, AFAIR.  This is presumably because it is a gratuitous
variation: it doesn't save space or satisfy any other syntactic or
semantic requirement.  It also breaks the consistent feature that ISO
8601 formats consist only of printing characters, never spaces.  It is
commonly done simply for human familiarity, to more closely resemble
pre-standard date conventions.  I think applications that want to use
such non-standard variations can do their own conversions.

Interesting comparison: MySQL allows *any* non-digit character for the
"T" separator, and indeed for all the other separators.  It can do this
because it requires a particular sequence of elements: four-digit year,
month, day of month, hour, minute, second.  Any application that needs
to accept different element sequences needs to look at the prefix and
separators.

-zefram


Re: is YYYY-MM-DD HH:MM:SS an ISO8601 format?

2006-11-15 Thread Zefram
David Cantrell wrote:
>Isn't the timezone also required?

No.  Particular applications of ISO 8601 may require it, but ISO 8601
itself does not.  The standard allows great freedom in the choice
of elements, and concerns itself with how to represent the desired
combination of elements.  Mechanism, not policy.

-zefram


Re: Trouble with time

2006-11-28 Thread Zefram
Rafael Morales wrote:
>Why the seconds and milliseconds do not change ?? How can I do it to
>work fine ???

Your program looks up the current time once, at the start.  It then
repeatedly prints out that one time.  $time never changes.  You want to
move the call to time() inside the loop, so that it looks up the current
time each time round:

while(1) {
my $dt = DateTime->from_epoch( epoch => time() );
my $time = $dt->strftime( '%Y-%m-%d %H:%M:%S.%3N' );
print "$time\n";
}

-zefram


Re: Trouble with time

2006-11-28 Thread Zefram
Stupid trick to make it do the magic Rafael expected:

#!/usr/bin/perl

use warnings;
use strict;

{
package Tied_Strftime;
sub TIESCALAR {
my($class, $fmt) = @_;
return bless(\$fmt, $class);
}
sub FETCH {
use DateTime;
use Time::HiRes qw(time);
return DateTime->from_epoch(epoch => time)->strftime(${$_[0]});
}
}

tie my $time, "Tied_Strftime", "%Y-%m-%d %H:%M:%S.%3N";
while(1) { print "$time\n"; }

#-zefram


Re: Fwd: DateTime::Calendar::Christian::Liturgical

2006-12-11 Thread Zefram
Rick Measham wrote:
>I wrote a lot of this a few years back (1). But came a across a huge 
>problem: The more I researched, the more difficult it became. Not only 
>does every country use a different liturgical calendar, but every 
>Province and possibly down to parish. (Where the local Saint's day can 
>replace an 'Ordinary' Sunday)

Sounds like a job for subclassing.

>* You'll need to provide options for determining your dates based on 
>rules (Is Epiphany on Jan 6 or on Thursday?)

And mixins.

-zefram


Re: Indic calendars

2007-01-03 Thread Zefram
Jaldhar H. Vyas wrote:
>On this auspicious day of Poshi Poonam (full moon of the month of Pausha)
>I would like to announce that I'm working on implementing various 
>Calendars traditionally used in India for the DateTime API.

I didn't realise there were several.  I was fascinated by the lunisolar
one described in Calendrical Calculations.

>So I was planning on using the following namespaces:
...
>Does this seem ok?

Looks good to me.

-zefram


Re: Indic calendars

2007-01-03 Thread Zefram
Jaldhar H. Vyas wrote:
>Unfortunately the one described there doesn't really match up to anyone 
>used in real life.

Not all that surprising.  I found an awful lot of errors in that book
on the stuff that I know about.

-zefram


Re: changing Daylight Savings Times (DST) in 2007

2007-01-05 Thread Zefram
Aravind J wrote:
>Do we have  any patch for the Perl Date & Time module for new  Daylight 
> Savings Times (DST) changes in 2007 as per Energy Policy Act of 2005 

The latest DateTime::TimeZone, version 0.57, reflects this change.

-zefram


Re: changing Daylight Savings Times (DST) in 2007

2007-01-09 Thread Zefram
[EMAIL PROTECTED] wrote:
>   What was the first version of the
>Olson tz database to incorporate the legislated 2007 North American
>Daylight Saving Time changes?

2005l.

>  Is it correct to say that DateTime has
>been DST2007-ready since 2005?

Since version 0.38, released 2005-11-21.

-zefram


Re: ANNOUNCE: DateTime::TimeZone 0.58

2007-01-09 Thread Zefram
Matthew wrote:
>How can I ensure that all our linux servers are up to date with the 
>2007a Olson database?

The quick way for a single server is:

# perl -MCPAN -e'install DateTime::TimeZone'

>everyone out there have to update their zoneinfo to accommodate the new 
>time changes?

No.  You need to update if you're likely to process timestamps that are
affected by the latest updates.  Whether you're affected depends on what
version you're currently on and (obviously) what kind of work you do.
Unfortunately there doesn't seem to be much of a changelog for the
Olson database.

It won't hurt to upgrade anyway.

-zefram



Re: changing Daylight Savings Times (DST) in 2007

2007-01-09 Thread Zefram
Monty, James T wrote:
>This isn't obvious from the CHANGES files; in fact, it seems to
>contradict them.

Which file do you speak of?  I based my answer on the timezone
list archive <ftp://elsie.nci.nih.gov/pub/tzarchive.gz>,
which pointed at 2005l, and the DT::TZ Changes file
<http://search.cpan.org/src/DROLSKY/DateTime-TimeZone-0.58/Changes>,
which said that 2005n went into 0.38.

-zefram


Re: changing Daylight Savings Times (DST) in 2007

2007-01-09 Thread Zefram
Aha, I have discovered an error in the Changes file.  It says "based
on version 2005i of the Olson database" for both 0.36 and 0.37, but
those two versions actually have different rules for America/New_York.
0.37 has the new 2007+ rule, whereas 0.36 does not.  I suspect that 0.37
is actually based on version 2005l.

Anyway, for your FAQ: based on brief examination of the module files,
the first released version of DT::TZ that has the 2007+ US rule is 0.37,
dated 2005-08-22.

-zefram


Re: recurring system

2007-01-16 Thread Zefram
Matthew wrote:
>  To me, this seems amazingly inefficient. If my weekly recurring 
>original event start date was Aug 1, 2006, the perl would have to loop 
>26 times to find the next event (in this case, Jan 23).
>
>Can anyone offer a better solution to this?

Yes: determine the day-of-week of the original event, and the week
containing tomorrow, then put them together.  With my Date::ISO8601
module this looks like:

use Date::ISO8601 qw(ymd_to_cjdn cjdn_to_ywd ywd_to_cjdn present_ymd);

my $today_cjdn = ymd_to_cjdn(2007,1,16);
my $initial_cjdn = ymd_to_cjdn(2006,8,1);

(undef, undef, my $repeat_dow) = cjdn_to_ywd($initial_cjdn);
my($next_y, $next_w, $today_dow) = cjdn_to_ywd($today_cjdn + 1);
if($today_dow > $repeat_dow) {
($next_y, $next_w, undef) = cjdn_to_ywd($today_cjdn + 8);
}

print present_ymd(ywd_to_cjdn($next_y, $next_w, $repeat_dow)), "\n";

I expect the same algorithm can be implemented using DateTime, but I'm
not a regular user of that, sorry.

-zefram


Re: not correct timezone

2007-01-18 Thread Zefram
abhishek jain wrote:
>perl -e 'use DateTime;print DateTime->now()->hms()';
>
>i want to print the correct time but it is dispaying the previous timezone's
>time.

It's using the universal timezone (UTC), not paying attention to *any*
timezone setting.  You have to tell it to use a timezone:

perl -e 'use DateTime;print DateTime->now(time_zone => "local")->hms()';

-zefram


syntax to specify time scales

2007-01-20 Thread Zefram
ht also
be useful to allow the variable-length ISO 8601 period duration format, so
"+3m" (for +00:03) and "+76.3s" (for +00:01:16.3) would also be allowed.
There doesn't seem to be any parsing trouble in store there.

Getting more obscure: as a workaround for the limitation of the Olson
database, it might be useful to specify a base timezone and add onto
that the offset rules from a geographical civil timezone.  Legal London
civil time is essentially UT1 + offset_rule_of(Europe/London).  Should we
have a syntax for that?  Similarly, what about generating a new base time
scale by offsetting an existing one?  GPS time is TAI - 19s, so perhaps
similar cases should be specifiable in this manner.  But the syntax
must be conspicuously different from that used to add a timezone offset,
because this offset is part of the base time scale, not a timezone.

Finally, there are situations where only a base time scale is to be
specified, and timezones are not permitted.  Displaying an MJD is such
a situation.

Congratulations if you've made it this far through this message.

My experimental code is at

http://www.fysh.org/~zefram/time/purchron

For a multi-scale display, do

$ purchron 
'(fixed,UTC:)_(ymdhms,UTC,9,-1)/___(fixed,GPST:)_(ymdhms,GPST,9,-1)/(fixed,TAI:)_(ymdhms,TAI,9,-1)/(fixed,TT%28TAI%29:)_(ymdhms,TT_TAI,9,-1)'

-zefram


Re: syntax to specify time scales

2007-01-22 Thread Zefram
m too onerous.  And I hope that time scales
that are just fixed offsets from other time scales will not proliferate.

>I like it. I would like to reach a point where we can implement this in
>DateTime.

There are some considerations for that which you probably don't want to
influence DateTime.  In a real-time clock program, overhead is important.
You'll notice that purchron does some bignum stuff with the libraries,
but that's all relegated to the non-RT thread which researches stuff in
advance.  All the calculations in the main thread are done in open-coded
native integer arithmetic, to get the new display frame out as quickly
as possible.  I'm quite pleased that I get acceptable performance from
pure Perl, without having to resort to C.

What you should aspire to with DateTime is first to do all those
calculations, with a suitably abstracted interface.  Don't compromise
on the abstraction or functionality of DateTime to get its performance
up to what purchron achieves.

For my own libraries, I'm finding that I'm implementing several things in
real-time and non-real-time versions.  purchron is where all the real-time
stuff goes, though I might make another library out of that some day.

-zefram


Re: syntax to specify time scales

2007-01-22 Thread Zefram
Rick Measham wrote:
>my $riyadh_lmt = DateTime::TimeZone::LMT->new(
>longitude => 46 + (43 / 60) + (27 / 3600)
>);

How much error in time conversions is introduced by the floating point
rounding?

>A thought: it might be good to somehow include the ability to load other 
>TZ modules from a string. Eg. time_zone => "LMT/46d43m27s" would look 
>for LMT/46d43m27s.pm and failing that, drop back to looking for LMT.pm 
>and pass it's constructor the '46d43m27s'.

I'd say don't do that with "/", which should be reserved for hierarchical
naming.  Maybe parens, though.  But you don't need it for LMT if the
generic offset capability is implemented: you can always do LMT as a
"UT1+..." spec.

-zefram


Re: syntax to specify time scales

2007-01-22 Thread Zefram
Eugene van der Pijll wrote:
> it's the difference
>between GPS and TAI(GPS) that is fixed, right?

Forgot to reply to this earlier.

Yes, strictly speaking the equation is

TAI(GPS) = GPST + 19 s

I've been using the term "GPS time" slightly loosely, to refer to what
we might call GPST(TAI), defined as

GPST(TAI) = TAI - 19 s

TAI(GPS) stays within about 10 ns of TAI.  In purchron the difference
between GPST and GPST(TAI) is swamped by the uncertainty with which TAI
is known, so I think I'm justified in dropping the "(TAI)" there.

-zefram


Re: syntax to specify time scales

2007-01-23 Thread Zefram
ses, I think we only want the database for
the first class of name (pure geographical).  If we're extending the
timezone namespace then we can decide for ourselves what to do for
backward compatibility.  Conveniently, the pure geographical names
follow a fairly restrictive syntax: no digits, no "+", no "-" in the
first component.

>User interface could be better,

Which aspect of it?  User friendliness was certainly not a major
consideration in writing it, but I'm open to suggestions.

>When do you plan to implement UT1 (using the published values, some
>interpolation and extrapolation, and some (no doubt inaccurate) equation
>for the deceleration of the earth's rotation? ;-)

I certainly do plan to do the astronomical time scales.  I'm gradually
researching sources of TT<=>UT1 data.  The IERS publishes measured daily
values of UT1-UTC, and each week publishes daily extrapolated values
for the next year.  That gives me recent history and near-term future.
For long-term future we have good models of the secular deceleration:
it's pretty linear.  Phase lock is lost, but interval calculations
will work.  Medium-term future I'm concerned about, because IERS has
the knowledge to extrapolate years out but doesn't publish either the
models or the predictions AFAICT.  For the past, going back a couple of
centuries, actual measurements exist, but I don't know where to find them.
For earlier recorded history there are some very isolated measurements
from eclipse records, also requiring research.  In prehistory we've got
archaeological and geological evidence of LOD, but of course no phase
synchronisation.

I'd like to put all this knowledge behind a consistent interface.
So tt_to_ut1() and ut1_to_tt() functions will hide the complexity of
multiple data sources.  Also want tai_to_ut1() and ut1_to_tai(), for
greater precision in the TAI era.  (UT1-TT(TAI) is known somewhat better
than UT1-TT, because of the uncertainty in TT(TAI)-TAI.)

I really want to have explicit uncertainty bounds as part of this
interface.  I ought to add them to the Time::TT conversions too: they're
conspicuously missing, but unfortunately the uncertainty data is also
conspicuously missing from the data files that it downloads from BIPM.

The real-time version of UT1 in purchron only needs to look at IERS
forecasts.  Ideally I'd like to do that by using the non-real-time
tai_to_ut1() in the background and then doing piecewise linear
interpolation in the display generator.  But since handling the IERS
forecasts alone is much simpler than the full UT1 module, I might do
the IERS bit on its own in purchron first.

-zefram


Re: syntax to specify time scales

2007-01-23 Thread Zefram
For reference, the POSIX definition of $TZ syntax is at
<http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html>.
In summary, POSIX defines the meaning of $TZ strings matching $posix_tz
below:

$abbrev = qr#[A-Za-z]{3,}|\<[-+0-9A-Za-z]{3,}\>#;
$offset = qr#[-+]?([01]?\d|2[0-4])(:[0-5]\d(:[0-5]\d)?)?#;
$rule_date = qr#J0*([1-9]|\d\d|[12]\d|3([0-5]\d|6[0-5]))
   |0*(\d|\d\d|[12]\d|3([0-5]\d|6[0-5]))
   |M0*([1-9]|1[0-2])\.0*[1-5]\.0*[0-6]#x
$rule_time = qr#([01]?\d|2[0-3])(:[0-5]\d(:[0-5]\d)?)?#;
$rule_dt = qr#${rule_date}(/${rule_time})?#o;
$posix_tz = qr#${abbrev}${offset}
   (${abbrev}(${offset})?(,${rule_dt},${rule_dt})?)?#xo;

(Some of the syntax isn't stated with full clarity in the standard,
so a bit of interpretation went into the above.)  It also states that
any string starting with ":" has implementation-defined meaning.

Interaction with the Olson syntaxes:

* all Olson syntaxes that include a "/" are disjoint from POSIX ($posix_tz
  can include a "/", but if so then there must be a digit earlier in
  the string, which Olson never does)

* the US ones such as "EST5EDT" are backward compatibility hacks for
  the same syntax that POSIX also allows for backward compatibility
  (POSIX doesn't actually define when DST is taken to start and end if
  the rule is not explicitly given)

* the short names qr#GMT[-+]?0# overlap with POSIX and are interpreted
  the same by both

* other short names are disjoint from POSIX

Olson's fixed offsets from UT, with names like "Etc/GMT-5", don't appear
at the top level presumably because they clash with POSIX syntax.
POSIX would interpret "GMT-5" as meaning a timezone 5 hours *ahead*
of UTC, and bearing the name "GMT".

Rick: you suggested allowing a timezone string to give a module name
and constructor argument.  Interpreting POSIX timezone strings might be
a good application of this idea.

-zefram


Re: syntax to specify time scales

2007-01-23 Thread Zefram
Eugene van der Pijll wrote:
>  Lord Tanlaw refers to the UK as the only
>industrialized country that has its legal time based on GMT. But there
>are probably a lot of former colonies that have not changed their time
>laws.

Found a source for more on this.  <http://www.ucolick.org/~sla/leapsecs/
onlinebib.html> has a section about it, with many links.  It explicitly
lists some countries where legal time is based on UTC: France, Germany,
Hong Kong, Korea, Netherlands, New Zealand, Sweden, and Switzerland.
It also explicitly lists some that are based on GMT: the European Union,
Ireland, Namibia, United Kingdom, United States (former colony!), and
the canadian provinces Ontario, Saskatchewan, and Quebec.  So the noble
Lord was in error on that point.

The context for that bibliography page is the debate over whether leap
seconds should continue.  One of the problems that would occur if UTC
were to have no more leaps is that UTC+offset would cease to be a good
approximation of legal time in countries where legal time is GMT+offset.
It's essentially impossible to get all the countries in the world to
legislate on anything.

I also searched the Olson database.  As we know it doesn't directly
address this topic, but I looked for offsets that were not in integral
minutes.  A fractional-minute offset is a dead giveaway for use of solar
time, because UTC can't accept such offsets.  The database doesn't list
any timezone with a current offset involving fractional minutes.  The last
usage of such an offset that it records was in Liberia, and ceased on
1972-05-01.  The database doesn't actually say much about Saudi Arabia;
the apparent-solar-time files are presented without real explanation.

I'd like to introduce for consideration the concept of explicitly vague
Universal Time.  The term "UT" on its own is ambiguous: it refers to UT1,
UT2, UTC, UT0, UTC-SLS, and others, collectively.  If one is working at
the minute level or above, the differences between the flavours of UT
are insignificant, so merely specifying "UT" suffices.  Unix timestamps
that predate precise UTC synchronisation are ambiguous in just this way,
and so Unix time in that era is best understood as being based on "UT",
rather than UTC or GMT specifically.  This seems to be the best way to
think about DateTime's current time scale too, when working outside the
range of known leap seconds.

I think this vague UT is what we should consider Olson timezones to
be based on, not UTC.  I asked the tz mailing list about base time
scales a while ago, and their answer supported this interpretation.
In the present era they consider the timezones to be based on "UTC"
(that's the term they use), but they don't really think about the meaning
of that, and they have no answer for what time scale they're based on
prior to UTC's existence.  It's the usual problem with interdisciplinary
timekeeping: three or four orders of magnitude difference is enough to
prevent most meaningful dialogue.

With that interpretation of the Olson database, an implementation is
free to use the database's offsets with whatever form of UT it finds
most convenient: most likely UTC.  Until the database does properly
address the issue, let's not perceive information that's not there.

-zefram


Re: syntax to specify time scales

2007-01-24 Thread Zefram
UT" in the above paragraph is complicated.  Nautical
timekeeping has been precise enough to distinguish between UTC and UT1
for longer than UTC has existed.  Sub-second timekeeping is essential for
precise astronomical navigation.  These timezones were originally based
specifically on GMT (UT1).  The DUT1 correction is published in order
to allow nautical use of UTC, so modern practice probably does use UTC.

ISO 8601


"Z" represents the base time scale.  An offset in hours and minutes may
be specified with the syntax qr/[-+]\d\d:\d\d/ (except that "-00:00"
is prohibited).

The standard mentions UTC in some places, but isn't actually specific to
that.  The textual formats are applicable to any use of the 24-hour clock.

-zefram


Re: missing something..part 2

2007-01-25 Thread Zefram
Matthew wrote:
>I see all the Etc/GMT+X inside my /usr/share/zoneinfo/Etc/ so what's up?

DateTime::TimeZone doesn't look in /usr/share/zoneinfo.  It has its own
version of the zoneinfo database, in the form of Perl modules, but it
doesn't include the Etc pseudo-zones.

>Are the Etc/GMT+X the proper "generic" time zones to use if your 
>country/region isn't specified?

Sort of.  Not quite.  If your timezone isn't in the database, you should
mail the database maintainers to get it added.

If you want to use a fixed offset from GMT/UTC/UT/whatever as your
timezone, Etc/GMT+X is the hack to use in the zoneinfo database, but
DateTime::TimeZone has a better mechanism for this.  To get 5 hours
behind UT, which is what Etc/GMT+5 does, pass in the timezone name
"-05:00".  Note the change of sign: for historical reasons the signs in
the Etc/GMT+X names are the opposite of the actual offset.

-zefram


Re: missing something..part 2

2007-01-25 Thread Zefram
Matthew wrote:
>   Hehe. I'm the DBA. And I've updated everything to most recent stuff, 
>but the boss man wants "generic time zones" in our list "just in case" a 
>customer can't find his or doesn't realize his city is listed differently.

Unlikely to ever occur.  As far as anyone knows, the Olson zoneinfo
database contains all currently-distinct timezones.  (It explicitly aims
to include all timezones that have been distinct at any time since 1970.)
You might want to play with the tzselect(8) program, which guides one
through selecting a geographical timezone from the database.

-zefram


SysV timezones in DT::TZ

2007-01-25 Thread Zefram
I've knocked together an implementation of SysV/POSIX
timezone specifications for DateTime.  Code is at
<http://www.fysh.org/~zefram/tmp/SystemV.pm>.  This is my first go at
implementing anything in the guts of DT, so experts here please check
that I'm using the interfaces correctly.  I don't quite follow what the
semantics are meant to be when a DT object has its timezone changed.

I'm not clear on which DateTime methods a timezone object can rely on,
in the arguments it is given to the _for_datetime methods.  I need to
do Gregorian arithmetic even if the DateTime isn't Gregorian.  I used
only ->utc_rd_values and ->local_rd_values, and then called out to my
Date::ISO8601 module to do the date arithmetic.

Is it OK for me to put this on CPAN?

-zefram


Re: SysV timezones in DT::TZ

2007-01-30 Thread Zefram
I wrote:
>I've knocked together an implementation of SysV/POSIX
>timezone specifications for DateTime.  Code is at
><http://www.fysh.org/~zefram/tmp/SystemV.pm>.

No comments from anyone, and only two people have downloaded the code.
I presume, then, that those who looked at the code were happy with it,
and everyone else at least has no objection to the concept.

I'm also now working on a tzfile(5) reader in DT::TZ form.  I'll publish
that shortly; it's operational but the code's just a bit rough at
the moment.

The motivation for these modules is that I think the DT::TZ ensemble ought
to be able to do everything that libc does to interpret $TZ settings.
It's a digression from the question of $TZ syntax.  (Most of the stuff
I work on is a digression from something.)

-zefram


$TZ parsing

2007-01-30 Thread Zefram
Summary of <[EMAIL PROTECTED]>: various parsers of $TZ
allow these syntaxes:

glibc  Olson Solaris HP-UX  DT:TZ
  y0 y  y  y  n
  y0 n  n  y  n:
  y  y  y  n  n/
  y  y  y  n  n:/
  y  y  y  n  y
  y  y  y  n  n:
  n  n  n  n  yfloating
  n  n  n  n  yZ
  n  n  n  n  n1   

Note 0: glibc does a subset of POSIX-extended SysV syntax, not the
whole thing.

Note 1: looks like DT::TZ might have been meant to accept an offset
here, but in fact it is rejected.  An offset is accepted in the "name"
constructor argument, but not in $TZ.

The handling of leading ":" is interesting.  All the parsers that
accept it at all have at least one syntax that is accepted both with and
without it.  So, although POSIX specifies it as introducing completely
implementation-defined syntax, the tendency is for it to allow a different
subset of syntaxes; the discernible meaning of a $TZ string is unaffected
by a leading ":".

I think generally DT::TZ would be better if it parsed $TZ in a manner
more like the various libcs.  At the moment the Olson names are the
only syntax that it has in common with them.  As I've mentioned, I'm
developing modules that will handle SysV syntax and tzfiles, so these
could be used to parse the other kinds of $TZ string.  I think a leading
":" should also be accepted and ignored, for compatibility with the
libcs that accept it for some syntaxes.

-zefram


Re: SysV timezones in DT::TZ

2007-02-01 Thread Zefram
I wrote:
>I'm also now working on a tzfile(5) reader in DT::TZ form.

Trial code is at <http://www.fysh.org/~zefram/tmp/Tzfile.pm>.

If there are no objections, I'll put DT::TZ::SystemV on CPAN at the
coming weekend, and DT::TZ::Tzfile the next weekend.

-zefram


Re: SysV timezones in DT::TZ

2007-02-04 Thread Zefram
DateTime::TimeZone::SystemV is now on CPAN.

-zefram


Re: in search of a method of determining time and date info in various parts of the world

2007-02-08 Thread Zefram
Virden, Larry W. wrote:
>But how do I turn the user supplied date and time into the time for the
>various timezones, and figure out whether daylight savings time is in
>effect?

DateTime can do this:

#!/usr/bin/perl

use warnings;
use strict;

use DateTime;
use DateTime::TimeZone;

my $dt = DateTime->new(year => 2006, month => 3, day => 13, hour => 13,
time_zone => "America/New_York");

foreach (qw(
America/New_York America/Los_Angeles America/Denver
America/Chicago Europe/Zurich Europe/London Asia/Jerusalem
Asia/Tokyo Europe/Moscow Australia/Melbourne
)) {
my $tz = DateTime::TimeZone->new(name => $_);
$dt->set_time_zone($tz);
print $dt->iso8601, " ", $_, " (",
$tz->is_dst_for_datetime($dt) ? "DST" : "std",
")\n";
}

-zefram


Re: SysV timezones in DT::TZ

2007-02-10 Thread Zefram
DateTime::TimeZone::Tzfile is now on CPAN.

-zefram


revision of DT::TZ name parsing

2007-02-11 Thread Zefram
Attached is a patch that revises timezone name parsing in DT::TZ.
The changes:

Anything that looks like a System V timezone string is treated as such,
via DT::TZ::SystemV.  This interpretation overrides some of the zones
and links in the Olson database, and a couple that were in DT::TZ only.
In each case, of course, the overridden names existed for ad hoc backwards
compatibility with System V.

A filename (beginning with "/") is accepted as a timezone name, and
the file is parsed by DT::TZ::Tzfile.  (To consider: how should this
facility behave on non-Unix systems?  It would be nice to keep leading
"/" as the signal to the parser that a filename is coming up.  So the
main options are (a) parse a Unix-like filename and translate to local
form and (b) accept "/" followed by a local-form filename.)

A leading ":" is accepted and ignored in any timezone name except for
the meta-name "local".  This provides compatibility of $TZ parsing with
the various libcs.

If a timezone name can't be parsed at all, the error comes from DT::TZ
rather than DT::TZ::OffsetOnly.  This makes the message less confusing,
and is a cleaner arrangement.

Embedded "::"s are not allowed in place of "/" in Olson zone names.
That was an undocumented capability that didn't apply to link names.

In $TZ, *any* name other than "local" is accepted, as the documentation
already stated.  Previously a character set limitation was applied which
prevented the use of the numerical offset syntax.

The handful of non-SysV single-part zone (not link) names in the
Olson database are implemented differently.  Formerly the EU ones were
filtered out and the rest (the US ones) were accepted as first-class
zones.  Now the US ones, which are all fixed offsets, are implemented
as aliases to System V strings, and the rest (the EU ones) are thrown
away.  Little difference in behaviour, but internally cleaner.  Now no
single-part Olson zone names are used as such.

The documentation about name parsing is now more accurate.

-zefram


Re: revision of DT::TZ name parsing

2007-02-11 Thread Zefram
I wrote:
>Attached is a patch that revises timezone name parsing in DT::TZ.

Except that I forgot to attach it.  D'oh.  Really attached this time.

After applying the patch you'll need to do a "./tools/parse_olson --clean".

-zefram
diff -ur dttz-0.59/Build.PL dttz-mod0/Build.PL
--- dttz-0.59/Build.PL  2007-01-18 15:57:56.0 +
+++ dttz-mod0/Build.PL  2007-02-10 18:04:20.0 +
@@ -27,6 +27,8 @@
 requires=> { 'Params::Validate' => 0.72,
  'Class::Singleton' => 1.03,
  'Pod::Man'=> 1.14,
+ 'DateTime::TimeZone::SystemV' => 0.000,
+ 'DateTime::TimeZone::Tzfile' => 0.000,
},
 build_requires => { 'Module::Build' => 0 },
sign=> 1,
diff -ur dttz-0.59/lib/DateTime/TimeZone/Local.pm 
dttz-mod0/lib/DateTime/TimeZone/Local.pm
--- dttz-0.59/lib/DateTime/TimeZone/Local.pm2007-01-18 15:57:56.0 
+
+++ dttz-mod0/lib/DateTime/TimeZone/Local.pm2007-02-11 11:01:58.246367883 
+
@@ -237,7 +237,7 @@
 return 0 unless defined $_[0];
 return 0 if $_[0] eq 'local';
 
-return $_[0] =~ m,^[\w/\-\+]+$, ? 1 : 0;
+return 1;
 }
 
 
diff -ur dttz-0.59/lib/DateTime/TimeZone/OlsonDB.pm 
dttz-mod0/lib/DateTime/TimeZone/OlsonDB.pm
--- dttz-0.59/lib/DateTime/TimeZone/OlsonDB.pm  2007-01-18 15:57:56.0 
+
+++ dttz-mod0/lib/DateTime/TimeZone/OlsonDB.pm  2007-02-11 10:48:55.524500689 
+
@@ -111,8 +111,6 @@
 $name = shift @items;
 }
 
-return if $name =~ /[WCME]ET/ && ! $self->{backwards_compat};
-
 @obs{ qw( gmtoff rules format until ) } = @items;
 
 if ( $obs{rules} =~ /\d\d?:\d\d/ )
diff -ur dttz-0.59/lib/DateTime/TimeZone.pm dttz-mod0/lib/DateTime/TimeZone.pm
--- dttz-0.59/lib/DateTime/TimeZone.pm  2007-01-18 15:57:56.0 +
+++ dttz-mod0/lib/DateTime/TimeZone.pm  2007-02-11 11:00:36.820591025 +
@@ -6,10 +6,6 @@
 $VERSION = '0.59';
 
 use DateTime::TimeZoneCatalog;
-use DateTime::TimeZone::Floating;
-use DateTime::TimeZone::Local;
-use DateTime::TimeZone::OffsetOnly;
-use DateTime::TimeZone::UTC;
 use Params::Validate qw( validate validate_pos SCALAR ARRAYREF BOOLEAN );
 
 use constant INFINITY =>   100 ** 1000 ;
@@ -24,8 +20,6 @@
 use constant IS_DST  => 5;
 use constant SHORT_NAME  => 6;
 
-my %SpecialName = map { $_ => 1 } qw( EST MST HST EST5EDT CST6CDT MST7MDT 
PST8PDT );
-
 sub new
 {
 my $class = shift;
@@ -33,66 +27,81 @@
   { name => { type => SCALAR } },
 );
 
-if ( exists $DateTime::TimeZone::LINKS{ $p{name} } )
+if ( $p{name} eq 'local' )
 {
-$p{name} = $DateTime::TimeZone::LINKS{ $p{name} };
+require DateTime::TimeZone::Local;
+return DateTime::TimeZone::Local::local_time_zone();
 }
-elsif ( exists $DateTime::TimeZone::LINKS{ uc $p{name} } )
+
+my $name = $p{name};
+$name =~ s/\A://;
+
+if ( exists $DateTime::TimeZone::LINKS{ $name } )
 {
-$p{name} = $DateTime::TimeZone::LINKS{ uc $p{name} };
+$name = $DateTime::TimeZone::LINKS{ $name };
 }
-
-unless ( $p{name} =~ m,/,
- || $SpecialName{ $p{name} }
-   )
+elsif ( exists $DateTime::TimeZone::LINKS{ uc $name } )
 {
-if ( $p{name} eq 'floating' )
-{
-return DateTime::TimeZone::Floating->new;
-}
-
-if ( $p{name} eq 'local' )
-{
-return DateTime::TimeZone::Local::local_time_zone();
-}
-
-if ( $p{name} eq 'UTC' || $p{name} eq 'Z' )
-{
-return DateTime::TimeZone::UTC->new;
-}
-
-return DateTime::TimeZone::OffsetOnly->new( offset => $p{name} );
+$name = $DateTime::TimeZone::LINKS{ uc $name };
 }
 
-my $subclass = $p{name};
-$subclass =~ s/-/_/g;
-$subclass =~ s{/}{::}g;
-my $real_class = "DateTime::TimeZone::$subclass";
-
-die "The timezone '$p{name}' in an invalid name.\n"
-unless $real_class =~ /^\w+(::\w+)*$/;
-
-unless ( $real_class->can('instance') )
+if ( $name eq 'UTC' || $name eq 'Z' )
 {
-eval "require $real_class";
+require DateTime::TimeZone::UTC;
+return DateTime::TimeZone::UTC->new;
+}
+elsif ( $name =~ m#\A[-\w]+(?:/[-\w]+)+\z# )
+{
+my $subclass = $name;
+$subclass =~ s/-/_/g;
+$subclass =~ s{/}{::}g;
+my $real_class = "DateTime::TimeZone::$subclass";
 
-if ($@)
+unless ( $real_class->can('i

API for multiple time scales

2007-02-12 Thread Zefram
I've published some code at

http://www.fysh.org/~zefram/time/time_scale_expt

which is where I am with regard to processing diverse time scales.
I'll do something akin to that in purchron when it's all settled, and
I'd like to do something similar in non-real-time Perl modules too.
Comments are very welcome.

I've been working on the API more than the previously-discussed
naming issues.  Hence the names used in the published code are all
very tentative.

-zefram


Re: ANNOUNCE: DateTime 0.60

2007-02-13 Thread Zefram
Matthew wrote:
>Should I also be updating my /usr/share/zoneinfo/ and my MySQL tables 
>with the new Olson db so that everything is current?

In principle yes, but not for DateTime's sake.  DateTime::TimeZone doesn't
use those sources of timezone data.

If you were on 2007a then there's no need to upgrade at all: the only
change is the update of the copy of Bulletin C in a comment in the
leapseconds file.  No actual data has changed.

-zefram


Re: revision of DT::TZ name parsing

2007-02-19 Thread Zefram
Dave Rolsky wrote:
>First, I don't want to make these new modules requirements. What I'd 
>prefer to do is just load them if they exist and use them. No one (that I 
>recall) has asked for Posix or binary file support, so making them 
>dependencies seems like overkill.

OK.  But it seems a pity that a default install won't be able to parse
all $TZ settings that are valid for libc.  Actually there have been
two DT::TZ users who wanted their System V timezone strings handled
(see bottom of this message).

>Given that, I don't want to change the handling of these names. This would 
>be a backwards-incompatible change as it is, since in the Olson database a 
>name like EST5EDT uses the US rules, meaning it includes many historical 
>changes.

Fair enough.  Using US rules is a valid interpretation of "EST5EDT"
as a System V string anyway.

>This should probably use File::Spec->file_name_is_absolute() to figure 
>this out. None of the currently valid names are absolute file names on any 
>system (I think).

That would be a bad idea: it would mean that unknown filename syntaxes
constrain what system-independent syntaxes we can use in the future.
It's theoretically totally unworkable.  Anyway, among what we do know,
on the Mac any string that doesn't start with ":" is a valid absolute
filename.

I found a better way: URI::file can translate between filename syntaxes.
We can specify that the $TZ setting is a Unix-style filename, and it gets
translated as appropriate.  On Windows you can do "/c:/etc/localtime"
and the file C:\etc\localtime is used, and "/etc/localtime" translates
to the not-quite-absolute \etc\localtime.

>It seems like a leading colon for other things (like "EST5EDT") would be 
>an error. Of course, removing it isn't a big deal.

I addressed this issue in my survey of $TZ parsing.  glibc and HP-UX
both accept System V strings both with and without leading ":".  glibc,
Olson, and Solaris all accept filenames (including Olson names) both
with and without ":".  Basically, the consensus $TZ syntax is the same
both with and without ":".  Consistently ignoring the leading ":" is
where the trend is heading.

>1. Make SystemV & Tzfile entirely optional.
>2. Use the Olson versions of existing SystemV names (follows from 1)
>3. Use File::Spec->file_name_is_absolute to determine if a name could be a 
>path to a tz file.

Patch attached does 1 and 2, and does filename translation instead of 3.
I extended 2 to the non-SysV single-part Olson names that my previous
patch implemented as links to SysV strings.

There were also two SysV-style strings that were never in Olson, but which
DT::TZ had as links to Olson zones.  These are "AKST9AKDT" and "JST-9".
They were added to satisfy particular DT::TZ users who were using these
$TZ settings.  As they're not in Olson, I reckon these should be treated
as any ordinary SysV string, so this patch has no grandfathering for them.
Those users will have to install DT::TZ::SystemV.  Grandfathering for
them is easily added if you really want to.

In other respects it's equivalent to the previous patch.  As before
you'll need to rerun tools/parse_olson, to pick up the changes to
TimeZoneCatalog.pm.

-zefram
diff -ur dttz-0.6101/lib/DateTime/TimeZone/Local.pm 
dttz-mod1/lib/DateTime/TimeZone/Local.pm
--- dttz-0.6101/lib/DateTime/TimeZone/Local.pm  2007-02-18 15:54:12.0 
+
+++ dttz-mod1/lib/DateTime/TimeZone/Local.pm2007-02-19 19:30:30.018396544 
+
@@ -92,7 +92,7 @@
 return 0 unless defined $_[0];
 return 0 if $_[0] eq 'local';
 
-return $_[0] =~ m{^[\w/\-\+]+$};
+return 1;
 }
 
 
diff -ur dttz-0.6101/lib/DateTime/TimeZone/OlsonDB.pm 
dttz-mod1/lib/DateTime/TimeZone/OlsonDB.pm
--- dttz-0.6101/lib/DateTime/TimeZone/OlsonDB.pm2007-02-18 
15:54:11.0 +
+++ dttz-mod1/lib/DateTime/TimeZone/OlsonDB.pm  2007-02-19 00:02:04.0 
+
@@ -111,8 +111,6 @@
 $name = shift @items;
 }
 
-return if $name =~ /[WCME]ET/ && ! $self->{backwards_compat};
-
 @obs{ qw( gmtoff rules format until ) } = @items;
 
 if ( $obs{rules} =~ /\d\d?:\d\d/ )
diff -ur dttz-0.6101/lib/DateTime/TimeZone.pm dttz-mod1/lib/DateTime/TimeZone.pm
--- dttz-0.6101/lib/DateTime/TimeZone.pm2007-02-18 15:54:12.0 
+
+++ dttz-mod1/lib/DateTime/TimeZone.pm  2007-02-19 20:19:13.383139216 +
@@ -6,10 +6,6 @@
 $VERSION = '0.6101';
 
 use DateTime::TimeZoneCatalog;
-use DateTime::TimeZone::Floating;
-use DateTime::TimeZone::Local;
-use DateTime::TimeZone::OffsetOnly;
-use DateTime::TimeZone::UTC;
 use Params::Validate qw( validate validate_pos SCALAR ARRAYREF BOOLEAN );
 
 use constant INFINITY =>   100 ** 1000 ;
@@ -24,7 +20,8 @@
 use constant IS_DST  =&

nautical timezones for DT::TZ

2007-02-19 Thread Zefram
Bonus patch: this adds handling of the nautical single-letter timezone
names to DT::TZ::OffsetOnly and DT::TZ.  Very simple feature, bound to
be useful to someone.

-zefram
diff -ur dttz-mod1/lib/DateTime/TimeZone/OffsetOnly.pm 
dttz-mod2/lib/DateTime/TimeZone/OffsetOnly.pm
--- dttz-mod1/lib/DateTime/TimeZone/OffsetOnly.pm   2007-02-18 
15:54:12.0 +
+++ dttz-mod2/lib/DateTime/TimeZone/OffsetOnly.pm   2007-02-19 
22:13:36.809631560 +
@@ -11,6 +11,14 @@
 use DateTime::TimeZone::UTC;
 use Params::Validate qw( validate SCALAR );
 
+my %letter_offset = (
+   A =>  +1,  B =>  +2,  C =>  +3,  D =>  +4,  E =>  +5,  F =>  +6,
+   G =>  +7,  H =>  +8,  I =>  +9,  K => +10,  L => +11,  M => +12,
+   N =>  -1,  O =>  -2,  P =>  -3,  Q =>  -4,  R =>  -5,  S =>  -6,
+   T =>  -7,  U =>  -8,  V =>  -9,  W => -10,  X => -11,  Y => -12,
+   Z => 0,
+);
+
 sub new
 {
 my $class = shift;
@@ -18,7 +26,8 @@
   } );
 
 my $offset =
-DateTime::TimeZone::offset_as_seconds( $p{offset} );
+$p{offset} =~ /\A[A-IK-Z]\z/ ? 3600 * $letter_offset{$p{offset}} :
+DateTime::TimeZone::offset_as_seconds( $p{offset} );
 
 die "Invalid offset: $p{offset}\n" unless defined $offset;
 
@@ -100,9 +109,13 @@
 
 =head2 DateTime::TimeZone::OffsetOnly->new ( offset => $offset )
 
-The value given to the offset parameter must be a string such as
-"+0300".  Strings will be converted into numbers by the
-C function.
+Three forms are accepted for the offset parameter.  It may be an offset in
+hours, minutes, and optional seconds, each two digits, with no separators
+and optional leading sign, for example "+0300".  It may be an offset in
+hours (one or two digits), minutes (two digits), and optional seconds
+(two digits), with ":" separators and optional leading sign, for example
+"-1:01:06".  Or it may be a single capital letter other than "J", in
+which case it is interpreted as a nautical timezone name.
 
 =head2 $tz->offset_for_datetime( $datetime )
 
diff -ur dttz-mod1/lib/DateTime/TimeZone.pm dttz-mod2/lib/DateTime/TimeZone.pm
--- dttz-mod1/lib/DateTime/TimeZone.pm  2007-02-19 20:19:13.383139216 +
+++ dttz-mod2/lib/DateTime/TimeZone.pm  2007-02-19 22:08:10.911550810 +
@@ -95,7 +95,8 @@
 require DateTime::TimeZone::Tzfile;
 return DateTime::TimeZone::Tzfile->new( $filename );
 }
-elsif ( $name =~ /\A[-+]?(?:\d\d?:\d\d(?::\d\d)?|\d{4}(?:\d\d)?)\z/ )
+elsif ( $name =~ /\A(?:[-+]?(?:\d\d?:\d\d(?::\d\d)?|\d{4}(?:\d\d)?)|
+   [A-IK-Z])\z/x )
 {
 require DateTime::TimeZone::OffsetOnly;
 return DateTime::TimeZone::OffsetOnly->new( offset => $name );
@@ -606,6 +607,13 @@
 
 =item *
 
+If the name is a single capital letter, other than "J", it is treated as
+a nautical timezone name, and a C object
+is returned.  (The case of "Z" is also handled by one of the rules above.
+It means the same thing either way.)
+
+=item *
+
 If the name is "floating", then a
 C object is returned.  A floating time
 zone does have I offset, and is always the same time.  This is


Re: revision of DT::TZ name parsing

2007-02-20 Thread Zefram
Dave Rolsky wrote:
>This is actually more bizarre then just having a leading colon, I think. 

As previously mentioned, a leading colon is no good for signalling syntax.
If you want to go that route, pick some other leading character to signal
a native filename.  Bizarreness discussed below.

>It might be preferable to require an actual file URI with scheme 
>(file:///etc/local/time or file://C:\foo\bar)

I pondered doing something like this.  With the leading "/", it would be
easy to interpret the whole string as a URI path and translate that into
local syntax.  Both of the examples that I gave would come out the same.
The disadvantage is that things like "/a%20b" then wouldn't act like
Unix filenames on Unix.

URI syntax has the major advantage of being already familiar to a great
many users across all OSes.  "file:///c:/foo/bar" gets done on Windows,
for example.  For this reason I think any form of "/"-delimited syntax
is not hugely strange to Windows users.  By the way, I just tried, and
"/c:\foo\bar" does get translated to "c:\foo\bar" by the current code,
so it turns out the user doesn't have to use "/" delimitation after all.

As for using a complete URI with scheme name, I see three problems.
The deep problem is that users will expect us to support downloading
tzfiles from arbitrary URIs of any scheme, which is not the plan.
The trivial problem is that URI scheme name syntax makes URIs clash with
some of the syntaxes we already support, so we'd need some other flag
character in front of the URI.  Finally, you're talking about inventing
a completely new facility here, whereas the "/"-prefixed Unix filename
is an existing widely-supported $TZ syntax.

I'd like to know if there's any precedent for timezone setting by filename
on Mac or Windows.

>I'd love to hear from the original requestor, but I'd guess that they'd 
>prefer to use the correct modern rules, as opposed to a to-the-POSIX-spec 
>interpretation.

The POSIX spec doesn't say what DST rules apply when they're not
explicitly stated in the timezone string.  Any rules at all are
POSIX conformant.  For this reason people should not be relying on any
particular behaviour from such strings, and should be giving a full rule.
The Alaskan user should be using "AKST9AKDT,M3.2.0,M11.1.0" if ey wants
to go the SysV route.

So I really don't care which behaviour we provide in the unspecified case.
I readily defer to your judgement concerning grandfathering of these
special cases.

>Honestly, supporting these POSIX time zones seems of academic interest or 
>useful for emulating some old system's behavior, but if you're writing new 
>code, it doesn't seem like a good idea at all,

My objective in this is merely to support all $TZ settings that the
various libcs do.  System V strings are an important subcase.

-zefram


Re: API for multiple time scales

2007-02-20 Thread Zefram
I wrote:
>http://www.fysh.org/~zefram/time/time_scale_expt

Version 0.001 is up.  This release does Martian time, including
the timezones used by the rover missions.  (Martian rotation is much
easier to model than Terran, because Mars has no large moon, tectonics,
or oceans, all of which make the Earth's rotation rather variable.)
I also implemented Eugene's idea of time scales modelling Unix time
with the discontinuities at leap seconds.  I already had an interface
for linear counts of seconds, so this just slots in.

I also implemented the leading proposed calendar for Mars in the module
Date::Darian::Mars, which is on CPAN.  That should plug right into the
experimental code.  (I haven't actually tried it.)

So I've now got parallel sets of day counts for Terra and Mars.  There
are some people already tracking time on Titan (and there's a Darian
calendar for it), so there might already be a day count system there.
Obviously we're going to get such systems on many many planets and moons.
On some moons we'll want to count rotations relative to the planet,
as well as (or instead of) rotations relative to Sol.  There's also
sidereal days to consider, which we have a canonical count for on Earth
(Earth Rotation Angle).  We use the 24:60:60 clock for subdivisions of all
of these types of day (at least so far), although we handle the integer
parts in completely different ways.  I think some factoring is in order.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Anthony R. J. Ball wrote:
>  I want to be able to grab the actual DST change dates for a timezone
>and year.

Some of what I'm doing would benefit from this too.  I suggest an addition
to the DT::TZ API:

=head2 $tz->boundary_for_datetime($dt, $which)

Given a C object, this method returns another C
object, in the UTC timezone, which identifies a boundary between
observances in this timezone.  Within a single observance, the offset,
short name, DST flag, and any other such attributes, remain constant.

C<$which> specifies which boundary is to be returned.  If 0,
the start of the current observance; if +1, the end of the current
observance; if -1, the start of the observance before the current one.
The "current observance" is the one that is in effect at the time
identified by C<$dt>.  If there is a boundary exactly at C<$dt>,
then the current observance is the one starting at that instance.

If an observance stretches infinitely far into the past or future,
C is returned for any non-existent boundary.

I'll do a patch to implement this if no one objects.

-zefram


Re: ANNOUNCE: DateTime::TimeZone 0.62

2007-03-06 Thread Zefram
Dave Rolsky wrote:
>- This release is based on version 2007c of the Olson database.

Where do we stand regarding my proposed changes?  Are you waiting for
anything from me?

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Anthony R. J. Ball wrote:
>  It sounds like a reasonable solution, as long as plugging
>the current end boundary +1 gives you the next boundary, so
>you can easily work your way forward.

That's the concept.  You'd use it like:

$dt = starting_point();
while(defined $dt) {
print $dt->iso8601, " ", $tz->offset_for_datetime($dt), "\n";
$dt = $tz->boundary_for_datetime($dt, +1);
}

which walks forward, or change "+1" to "-1" (and possibly add an initial
step with $which==0) to walk backward.

>maybe not to a lot of people, but the data is there, so
>why not make it available.

Quite.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Dave Rolsky wrote:
>I'd prefer multiple methods to a third parameter. Different names provides 
>clarity. Who would know what a number means without looking in the docs?

It's not meant to be used without reading the doc at least once.
Once read, of course, the $which values follow a logical plan and so
are memorable.

If you want three names, how about

prev_observance_start_for_datetime
this_observance_start_for_datetime
next_observance_start_for_datetime

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Anthony R. J. Ball wrote:
>  Are three even necessary?

Not absolutely, but with two when you want to find the start of the
current observance you'd have to use both methods and a date equality
comparison.  It'd be a pain, lots of people wouldn't bother doing it
properly and you'd get code that fails if given a date that is exactly
a boundary.  The three method way puts the effort where it belongs: in
the library, where it's actually easier to implement than the two methods.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Jim Bacon wrote:
>next_dst and prev_dst based on the year value of the DT object and a
>parameter specifying if what is wanted is the spring or fall date, or at
>least specifying change to daylight time or standard time.

Please no, not such a restrictive model.  That'd be a nightmare.  Timezone
offset changes are a lot more complicated than just a twice-yearly
alternation between two possibilities.  There are years with more than
two changes, and years with only one change or no change at all.

With the generic arrangement, where you get the next/previous observance
boundary working from an arbitrary DT, if you happen to know that your
timezone fits the twice-yearly-change pattern, you can do next from
the start of the year or previous from the end of the year to find the
two boundaries.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Eugene van der Pijll wrote:
>"Datetime" just means one point in time; it is not something that is
>observed.

Yes.  An offset (plus short name and DST flag) is observed.
The "_for_datetime" bit indicates the argument format, just like
"offset_for_datetime".

>What do you think of: "next_offset_transition"? Or even
>"next_transition", as it is always called on a timezone object, and it
>should be clear in that context what it is a transition of.

The latter is better, because it's not just the offset that transitions.
In some cases there is a transition without the offset changing, as for
example when the UK changed from British Summer Time to British Standard
Time, when the is_dst flag was the only thing that changed.

But with the "_transition" naming pattern there's a problem for the
names of the other two methods.  Logically the "prev_transition" could be
the start of the current or previous observance.  And "this_transition"
is meaningless.  If you want to drop the "_for_datetime" bit then just
go to "{prev,this,next}_observance_start": it is the observance, not
the transition, that is the referent of "this" and the others.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Jim Bacon wrote:
>Yes, provide methods for finding any type of transition besides DST, but I
>think DST might be the most common and should therefore have as many
>shortcuts as needed to simplify its use.

I'm not convinced that finding the DST change dates in a given Gregorian
calendar year is going to be a particularly common usage.  It seems
more likely that one would want to know the next observance (or offset)
change from the present, whether that's due to conventional DST or for
any other reason.

Anyway, here are my thoughts on non-general-case methods: I don't want
to have to reimplement them all in each independent timezone class.
Let's implement them generically, in terms of the general-case methods.
They don't, of course, strictly need to be methods; they can perfectly
well be ordinary functions in an independent module.  (So you can have
a module that looks up DST changes in a given Chinese lunar year, for
example.)  Anything that's of sufficiently wide use to justify being
a method can go into a base class that all timezone classes inherit.
Since DT::TZ contains the machinery for the parsed-Olson classes, I think
it should not itself be the mandatory base class; better to institute
a DT::TZ::base.

I'm principally interested in the general-case methods.  I think we
ought to establish the general-case inteface first, and do the syntactic
sugar later.  We have arbitrarily long to add more sugar methods to the
base class, but the underlying general methods form a contract between
independent bits of code, so other work depends on them.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Eugene van der Pijll wrote:
>So: perhaps it would be better to create a single method, returning a
>DateTime::Span object? And then make iterating methods that accept these
>spans:

That's a reasonable interface, if one can take DT::Span as ubiquitous.
It's certainly convenient if one really does want the interval rather than
the boundary.  Arguably also makes the method names more understandable
when dealing with the boundaries ($tz->observance($dt)->end, for example).

I suggest the method name "observance_span" instead of "observance", to
distinguish it from a hypothetical method that would return a reification
of the observance itself (as Dave mooted elsewhere on this thread).
Could also append "_for_datetime" if we want to fit in with the existing
so-named methods.

>Only problem is that DT::TimeZone would have a dependency on DT::Span
>now.

There's a circularity there: DT::Span's CPAN distribution depends on DT,
which depends on DT::TZ.  DT::TZ doesn't currently depend on DT.

DT::Span also looks quite heavyweight.  Brings in DT::Set, Set::Infinite,
and DT::Duration, even if one is only using it to wrap a pair of
endpoints.  Not sounding like such a good idea now.  Maybe we need
a DT::Span::Lite.

Another possibility: as the span-based interface is conceptually handy,
there's the option to have a lightweight DT-based interface in each
DT::TZ class, and put the heavy span-based interface in a base class,
wrapping the lightweight one.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Dave Rolsky wrote:
>However, something with some of the same info seems like a natural thing 
>to return from these methods.

So we'd have a DT::TZ::Observance class, with methods ->offset,
->short_name, ->is_dst, ->start_datetime, ->end_datetime.
$tz->observance_for_datetime($dt) would return one.

Re DT::Span (see other subthread), could have a ->span method that
loads DT::Span in all its glory and returns one.  Last-minute loading
would avoid bringing in so much mechanism when it's not being used.
Would have a distribution-time circular dependency issue though.

Would DT::TZ::Observance have methods ->next and ->prev?  Would make
walking the chain easy.  Raises questions of implementation strategy.
There's a general question of whether to cache the observance objects,
guarantee that the same ones are returned repeatedly, and so on.

If we're going to go the heavyweight route, this is the way to go.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Eugene van der Pijll wrote:
>Or: instead of making observance_for_datetime a method in the standard
>TZ distribution, make a separate distribution, with class
>DT::TZ::Observance (or maybe somewhere in DT::Util? DT::Ev?). 

Yes, that's probably better for the heavyweight approach.  Still need
a lightweight version to go in the timezone classes themselves.
Presumably we'll go for the lightest form then, just dealing with the
transition instants.

>DT::TZ would than have to implement just a single method to get at the
>raw data; for example DT::TZ->get_transition( $dt ), which returns the
>first transition after $dt, or the first transition if no $dt is passed.

I definitely want a backward iterator.  ->prev in DT::TZ::O will require
it, of course.  This comes back to the interface issues already discussed
for the boundary-based interface.

-zefram


Re: Help pulling dst change dates from DateTime::TimeZone

2007-03-06 Thread Zefram
Dave Rolsky wrote:
>Constants and magic values suck, names rule, d00d ;)

It's not a magic constant.  It's the index of the target observance,
relative to the current one.  I considered allowing it to take arbitrary
values (e.g., 3 to look three observances ahead), but people would misuse
that and end up iterating in O(n^2).  Hence a limited-range index.

With three differently-named methods, they'll actually end up being
implemented in terms of the single method anyway, it'll just be hidden.
I wouldn't let such a consideration alone dictate the nature of the
public API, of course.  But if we go the route of a separate heavyweight
mechanism, with a very lightweight mechanism in the classes themselves,
then there may be an advantage in keeping the interface (and unavoidable
implementation structure) as small as possible.

-zefram


Re: gmt<->cst off by an hour

2007-03-12 Thread Zefram
Matthew wrote:
>$dt->set_time_zone("US/Central");
>
>print "+9weeks CST: " . $dt->epoch . " - " . $dt->datetime . "\n";

This is wrong.  "US/Central" doesn't refer to CST specifically.  It refers
to the US central timezone *including DST switches*.  The date it's
showing is in CDT, not CST.  Try displaying $dt->time_zone_short_name
as well, which will show what's going on.

>Many of these events were created before the DST switch. How should I be 
>calculating these event dates so that 4PM 2 weeks ago still shows as 4PM 
>tomorrow?

Do the ->add with the timezone already set.  Thus:

#!/usr/bin/perl
use DateTime;
my $dt = DateTime->from_epoch(epoch => 1168812000, time_zone => 'GMT');
print "Date in GMT: " . $dt->epoch . " - " . $dt->datetime . " " . 
$dt->time_zone_short_name . "\n";
$dt->set_time_zone("US/Central");
print "Date in cen: " . $dt->epoch . " - " . $dt->datetime . " " . 
$dt->time_zone_short_name . "\n";
$dt->add(weeks => 9);
print "+9weeks cen: " . $dt->epoch . " - " . $dt->datetime . " " . 
$dt->time_zone_short_name . "\n";
$dt->set_time_zone("GMT");
print "+9weeks GMT: " . $dt->epoch . " - " . $dt->datetime . " " . 
$dt->time_zone_short_name . "\n";

yields:

Date in GMT: 1168812000 - 2007-01-14T22:00:00 UTC
Date in cen: 1168812000 - 2007-01-14T16:00:00 CST
+9weeks cen: 1174251600 - 2007-03-18T16:00:00 CDT
+9weeks GMT: 1174251600 - 2007-03-18T21:00:00 UTC

-zefram


Re: gmt<->cst off by an hour

2007-03-14 Thread Zefram
Matthew wrote:
>Thanks Zefram.  To combat this issue in the future, should we be storing 
>our customer's event information in our DB in GMT and doing all 
>calculations using GMT? or store in their local TZ and calculate with 
>local TZ?

That depends on what you need to do with the data.  If you want to do date
arithmetic that is affected by the customer's timezone then you definitely
need to store which timezone that is.  For any event information where
it is only the absolute time that matters, I recommend storing in GMT.
Perform calculations in GMT or local time depending on what type of
calculation you intend to perform.

>Our servers are all GMT if that makes any difference.

This is wise.

-zefram


Re: gmt<->cst off by an hour

2007-03-14 Thread Zefram
Matthew wrote:
>if not, add 1 week to event start. loop until current time is less than 
>new event start and return new event start.

If you're doing this in the local timezone, it is better to add N weeks
to the original event time (and increment N) than to add 1 week onto the
previous event time.  This minimises funny behaviour around DST changes.

>But when you throw in the adding of weeks to an original start date, 
>should the orig start date be in local TZ or GMT?

The customer will most likely want events occurring at, say, 11:00
every Monday in their local timezone.  For that you need to do the date
arithmetic in the timezone; the gaps between events will occasionally be
167 or 169 hours instead of the regular 168.  Some customers might want
events at 168-hour intervals regardless of timezone; you could cater
to that by giving them the option to specify an event in GMT instead of
their actual timezone.  You can handle both cases, therefore, with just
the local timezone logic.

-zefram


Re: DateTime and TimeZone question

2007-03-29 Thread Zefram
crap wrote:
>For the life of me I cannot figure this out. I am trying to look up the
>timezone based off an hour offset. Is there a way to do this with
>DateTime::TimeZone?

If you want a timezone that is just a fixed hour offset, then just do

$tz = DateTime::TimeZone->new->new(name => "-0400");

That's a timezone that's permanently four hours behind UT.

But I suspect that what you actually want is to put in "-0400" and get
back one of the civil timezones, with DST rules, that happens to be at
UT-4h currently.  There are several of these, such as America/New_York
and America/Cuiaba, so at best you'd expect to get a list of matching
timezones.  The timezones aren't indexed in this way, so you'd have to
iterate through the lot checking each.  DateTime::TimeZone->all_names
provides the list that you'll need to check through.

-zefram


Re: Parsing LDAP Generalized Time with DateTime::Format::ISO8601

2007-05-04 Thread Zefram
Ulrich Zehl wrote:
>Since I don't have access to the ISO standard, I cannot verify that the
>format as given in RFC 4517 is really standards compliant,

It's conditionally compliant.  The relevant paragraph of the standard
reads:

# NOTE  By mutual agreement of the partners in information interchange,
# the character [T] may be omitted in applications where there is no
# risk of confusing a date and time of day representation with others
# defined in this International Standard.

So an ISO 8601 parser shouldn't accept it by default.  Where context
dictates that a date-time representation is expected, it is unambiguous
and so parsing it this way is acceptable.

-zefram


Re: Confused: adding/subtracting hour from timestamp

2007-05-09 Thread Zefram
Bas Schulte wrote:
>print STDERR "Converted time from $hours:$minutes with offset=$offset  
>to " . $normalizedTs->strftime("%H:%S:00") . "\n";

%S is seconds, not minutes.  You might prefer to use the format "%T",
which is equivalent to "%H:%M:%S".

-zefram


Re: DateTime::Event again

2007-05-16 Thread Zefram
Simon Wistow wrote:
>I've started sketching out DateTime::Event - a module that would 
>represent a single, err, event.
...
>The problem we have here is that for one off events start and end are 
>definite points in time. For recurring events this merely represents the 
>earliest and latest instances of this event (which may both be 
>infinite).

A "recurring event" is not a single event.  It is a structured group of
events.  You should have distinct classes for single event and recurrence.
Most of the methods you list are applicable to one class or the other,
not both.

-zefram


Re: DateTime::Event again

2007-05-16 Thread Zefram
Simon Wistow wrote:
> In every calendaring system I've come across a 
>recurring event and a single event are represented as the same thing 
>only with different properties.

Bad OO design is very common.  Especially, I have found, where dates
and times are concerned.

>A recurring event is a single entity - 

Yes, sure it is.  It's worthy of being represented by a specific type
of object.  It's just that that's not the same type as a single event.

>it is one thing and should only be stored one time.

I did not intend otherwise.  It is certainly useful (and essential in
infinite cases) to represent a recurrence just as a single object, not
reifying each occasion.  Reifying a particular instance (as an event
object) should be done only when attention turns to the particular.
When the particular *is* interesting, though, one certainly does want
an object referring to that instance and not to all the other occasions
of recurrence.  That's what your "recurrences" method is about.

>Using your description, a one-off event could be described as a 
>structured group containing only one event.

You could certainly have such a trivial recurrence.  It's about as useful
as a one-element array.

>A recurring event can and will have all the same characteristics of a 
>single event

It does not have a specific start time, which is possibly the most
fundamental aspect of a single event.  Treatment of characteristics such
as attendees, which you listed, depends on your model of recurrence,
but you indicated a model in which a recurrence has just default values:
quite different semantics from the definitive values of a single event.
But most of your characteristics have only trivial semantics in the event
object context anyway: bad things to base your class hierarchy design on.

>It might be that a RecurrentEvent could be a subclass of SingleEvent.

That would indicate that a recurrent event is a single event, which is
not true.

>vice versa,

That's closer to the mark, treating single events as trivial recurrences.
It's like treating a character as a string of length one.  Perl does
the latter, but in general I think it's a bad strategy.

>or both are subclasses of a GenericEvent object,

Tricky to say what a GenericEvent actually represents in that case.

>more of an implementation detail rather than a conceptual detail.

On the contrary, it is of the most profound conceptual importance.

-zefram


Re: Possible Issue with DateTime->now()?

2007-05-23 Thread Zefram
Veripex, Inc. wrote:
>DateTime::now() does not appear to be accurate for me. I used to methods to
>prove this. The first method is creating a DateTime object using information
>provided by localtime(). The second is using the DateTime::now() method. The
>localtime method provides what I expected, the DateTime::now() method has an
>incorrect hour for me.

A DateTime object doesn't just represent a date and time of day, it also
has an attached timezone.  With DateTime->new you're giving it a date and
time in your local timezone and not specifying a timezone, so it only
knows the local time and that's all it can show you.  DateTime->now,
on the other hand, picks up the correct absolute time, but because you
didn't specify a timezone it doesn't know how you want it displayed.
It defaults to using the UTC timezone, and the hour that it shows you
is the current UTC hour.  You can see these timezone settings in your
dumps of the DateTime objects.

Neither of these timezone settings is what you want.  You most likely
want to use your local timezone in both cases.  Try adding the parameters
"time_zone => 'local'" in both constructor calls.

-zefram


Re: REQ: Additional 'truncate' option

2007-10-01 Thread Zefram
Dave Rolsky wrote:
> $dt->truncate( to => 'week' )->subtract( days => 1 );

That doesn't do what was requested: if you start with a Sunday then it
moves a week back rather than just going to the beginning of the day.
But this will work:

$dt->add( days => 1 )->truncate( to => 'week' )->subtract( days => 1 );

-zefram


Re: Patch for DateTime::Format::W3CDTF to respekt second fragment part

2007-11-07 Thread Zefram
Julian Haupt wrote:
>+if ($date =~ s/\.(\d+)$// )
>+{
>+  my $fraction = $1;
>+  $p{'nanosecond'} = (1 / $fraction) * 10**9;
>+}

That inversion can't be right.  Surely you mathematically want

$p{'nanosecond'} = "0.$fraction" * 10**9;

but actually the nanoseconds member is supposed to be an integer, and
it would be better to avoid floating-point arithmetic entirely:

$fraction = substr($fraction, 0, 9);
$fraction .= "0" x (9 - length($fraction);
$p{'nanosecond'} = 0 + $fraction;

-zefram


Re: Daylight-Saving Causes Twin Arrival Pickle

2007-11-08 Thread Zefram
Rick Measham wrote:
>http://www.wral.com/news/local/story/2011296/

When Parliament was debating the introduction of DST in the UK, as a
wartime measure in 1916, Lord Balfour (a member of the House of Lords,
the upper chamber of Parliament), with the usual aristocratic concern
for matters of inheritance, noted precisely this problem with it:

Supposing some unfortunate lady was confined with twins and one child
was born 10 minutes before 1 o'clock. ... The elder child would be
registered as being born at 12.50, but the younger child's birth,
ten minutes later, would be registered at 12.00. ... the time of
birth of the two children would be reversed. ... Such an alteration
might conceivably affect the property and titles in that House.

We can only hope that times of birth are annotated with the timezone
abbreviation where such ambiguity occurs.  I rather suspect that it's
not standard practice, though.  My understanding (from a British nurse)
is that hospital clocks are not especially accurate, so that they can't be
relied upon for minute-level accuracy anyway.  The birth times of me and
my three siblings (all single births) were all recorded as exact multiples
of five minutes, so I presume there's some rounding going on there,
but I'm at a loss to determine whether the rounding performed was down
or to nearest.  There's not much concern for precise timing in evidence.

-zefram


Re: Patch for DateTime::Format::W3CDTF to respekt second fragment part

2007-11-08 Thread Zefram
Jonathan Leffler wrote:
>Or, what happens if there are 10 digits after the decimal point.

That's what the substr() is for.

>Yeah, mostly academic, except I'm working towards a time type (not for 
>Perl per se) that extends down to picoseconds (and up to 10^12 years, 
>too).

At least 105 bits, then.  TAI64 covers nearly 10^12 years, of course,
and the extension TAI64NA goes down to attoseconds in a 128-bit format.
What's the concept behind your type?

Personally I like to use bignum rational arithmetic, for unlimited
resolution.  Performance is atrocious with the Perl bignum libraries,
unfortunately.

-zefram


Re: subtracting one date from another

2007-11-28 Thread Zefram
jagdish eashwar wrote:
>$dur = $date1->delta_days($date2);

This is correct as far as it goes.

>print $dur->days,"\n";
># This gives me - "4"

Here the duration is being expressed in weeks plus days, and you're
only looking at the days portion.  $dur->weeks returns 4; the duration
is 4 weeks plus 4 days.  To get the duration expressed purely in days,
do $dur->in_units("days").

-zefram


Re: adding days to date

2007-11-30 Thread Zefram
jagdish eashwar wrote:
>I came across some unexpected behaviour in datetime. In the following
>script, I first define $date1. Then I set $day1 = $date1. Then I add 2 days
>to $day1. Why does $date1 also get incremented?

Because a DateTime object doesn't represent a date-and-time per se; it
implements a variable that is typed to hold a date and time.  Walkthrough:

>my $date1 = DateTime->new(year => 2007,
> month => 12,
> day => 23);

You create a date/time variable, initialised as holding the date
2007-12-23.  You set $date1 to be a reference to that variable.

>my $day1 = $date1;

You copy the reference to the date/time variable from $date1 to $day1.
Both of these Perl variables now refer to the same date/time variable.

>$day1->add(days => 2);

You reference the date/time variable (via $day1), and tell it to modify
the date value that it holds by advancing it by two days.  The date/time
variable now holds the date 2007-12-25.

>print "day1 = ",$day1,"\n";   # gives me 2007-12-25 correctly.

You reference the date/time variable (via $day1), and ask it to display
the date that it holds (2007-12-25).

>print "date1 = ",$date1,"\n";  # why does $date1 also change to 2007-12-25?

You reference the date/time variable (via $date1), and ask it again to
display the date that it holds (2007-12-25).

The stage that should have rung alarm bells for you is

$day1->add(days => 2);

where you're doing date arithmetic calling a method on an object and
*ignoring its return value*.  This method operates by having a side effect
on the object.  If, instead, DateTime objects actually represented dates
and times per se, then there wouldn't be any of these side effects,
and this step in the program (combined with the previous step) would
instead be something like

my $day1 = $date1->plus(days => 2);

As it is, you need to create a second date/time variable, initialised
by copying the value from the first one.  So instead of

my $day1 = $date1;

do

my $day1 = $date1->clone;

Then the ->add call will side-effect this second date/time variable,
the one referenced by $day1, and leave the original date/time variable,
the one referenced by $date1, alone.

-zefram


Re: ANNOUNCE: DateTime::TimeZone 0.70

2007-12-03 Thread Zefram
Dave Rolsky wrote:
>  is one major change in this release, for the new Venezuelan time
>  zone.

What, again?  I thought he'd've learned the first time.

-zefram


Re: New Module - DateTime::NaturalLanguage

2008-01-12 Thread Zefram
Ken Irving wrote:
>  convert seconds into a "readable" format 344 => 5m44s
>
>This sounds similar to your module, except for being compact vs verbose.

I think Convert::Age doesn't qualify as processing natural language.
Its encoded format is actually the ISO 8601 duration format, although C::A
doesn't document that aspect and doesn't explain its interpretation of the
variable-size units.  Anyway, the ISO 8601 format, although derived from
English abbreviations, is meant to be language-neutral.  Luke's module
is definitively about English text, which is quite a different job.

-zefram


Re: DateTime->season?

2008-01-29 Thread Zefram
David E. Wheeler wrote:
>return $date lt '03-21' ? 'winter'
> : $date lt '06-21' ? 'spring'

Northern hemisphere chauvinism.

Aside from the hemisphere issue, the code is also wrong for tropical
latitudes, where the four-season system doesn't apply.  If you're trying
to answer a user's question of "what season is it?" then you need a lot
more flexibility.  Even within the four-season system, people draw the
boundaries in different places.  That's inevitable when trying to impose
sharp distinctions on a system that actually has a fuzzy continuum.

>Since seasons can sometimes change on the 22nd instead of the 21st,  
>this isn't quite right.

Sounds like you're looking for the equinox and solstice dates.  I don't
see any CPAN module providing this, on search.cpan.org.  I guess
that makes it your job to write one.  Ideally you'd want to work from
Astronomical Algorithms <http://www.willbell.com/math/mc1.htm>.  Google
for "equinox calculation" to find several lower-precision algorithms in
various programming languages.

-zefram


Re: Proposed module for the DateTime namespace

2008-04-07 Thread Zefram
Frazer Irving wrote:
>Second, I am planning make this module a subclass of DateTime::Span. Are
>there any problems with this approach I may be unaware of?

This seems silly.  Your comparison functions will be most useful if
they can be applied to DateTime::Span objects.  As far as I can see your
subclass isn't changing any behaviour of DateTime::Span, but is merely
a vehicle to provide access to your new functions that are a layer on
top of the existing behaviour.  The overall effect of such subclassing
is to prevent access to the new stuff in situations where it's applicable.

I suggest that your comparison predicates should be either standalone
functions (for which the module name "DateTime::Span::Compare" is an
excellent choice) or additional methods in the DateTime::Span class.
I think the former is the most sensible interface.

-zefram


Re: CET timezone

2008-08-18 Thread Zefram
Ton Voon wrote:
>timezones, but DateTime doesn't support the timezone CET. Is there a  
>reason for it?

The lettered abbreviations for timezones are generally ambiguous.
Most famously, there's an "EST" in both America and Australia.  I don't
think there's any "CET" other than Central European Time, though.
Generally these abbreviations are deprecated.

CET is almost certainly not what you want, though.  CET is a fixed
offset of UT+1h.  The civil time in west-central continental Europe is
UT+1h during the winter and UT+2h (Central European Summer Time, CEST)
during the summer.  I'm not aware of any short name for this CET/CEST
combination.  CET is year-round civil time only in certain equatorial
countries, such as Angola.

>   Is it better to specify a timezone based on a location,  
>such as Europe/Paris?

Yes.  These unambiguously refer to the civil time in a particular
location.

-zefram


Re: CET timezone

2008-08-18 Thread Zefram
Ton Voon wrote:
>server's timezone to CET.

Dodgy concept there.  It really doesn't make sense for a server as a
whole to have a default timezone other than UT.  A timezone choice is
a feature of certain types of application data, not a feature of where
you run the application.

>I can understand the arguments about EST, but if there's no ambiguity  
>about CET,

We just found an ambiguity about CET: does it include the DST rules?
Also, consider that your form of CET, with European-rules DST, is only
defined for 1977 and later (when the EU DST rules are defined), so it
falls apart if you need to process earlier times.  For those applications
you're forced to distinguish between Europe/Paris, Europe/Berlin, and
the others.  Not to mention that countries went onto CET-with-DST at
different times.

If you have some definite idea about what "CET" means in your
application context, go ahead and implement it.  That might be an alias
to Europe/Berlin, or a fixed offset from UT, or a synthetic timezone
(with its own tzfile) that doesn't match civil time anywhere.  But be
aware that it's not universally meaningful, and do it at the application
level without gumming up the implementation for everyone else.

>   I note that EST, MST  
>and HST are supported timezones: 

Historical oddities.  Not role models.

-zefram


Re: DateTime and Test::MockTime

2008-08-28 Thread Zefram
Mark Pitchless wrote:
>For some reason using DateTime->now doesn't get effected by using the
>Test::MockTime module to change the time.

It does if you load Test::MockTime before you load DateTime.

$ perl -MDateTime -MTest::MockTime=:all -lwe 'print time;print 
DateTime->now;set_relative_time(6000);print time;print DateTime->now' 
1219919095
2008-08-28T10:24:55
1219925095
2008-08-28T10:24:55
$ perl -MTest::MockTime=:all -MDateTime -lwe 'print time;print 
DateTime->now;set_relative_time(6000);print time;print DateTime->now'
1219919098
2008-08-28T10:24:58
1219925098
2008-08-28T12:04:58
$

-zefram


Re: Can someone try this to make sure I'm not mental?

2008-08-29 Thread Zefram
Kristian Flint wrote:
>This outputs "31-12-2008", which is in my mind is wrong. 

This is about timezones.  Look at other fields of the DateTime:

$ perl -lwe 'use Date::Manip; use DateTime::Format::DateManip; 
$date=ParseDate("1st January 2009"); print $date; my $dt = 
DateTime::Format::DateManip->parse_datetime($date); print $dt->dmy; print 
$dt->hms; print $dt->time_zone_long_name'
2009010100:00:00
31-12-2008
23:00:00
Europe/London
$

This is what happens if your $TZ (or system default timezone) is
Europe/London.  You can change it:

$ TZ=UTC perl -lwe 'use Date::Manip; use DateTime::Format::DateManip; 
$date=ParseDate("1st January 2009"); print $date; my $dt = 
DateTime::Format::DateManip->parse_datetime($date); print $dt->dmy; print 
$dt->hms; print $dt->time_zone_long_name'
2009010100:00:00
01-01-2009
00:00:00
UTC
$

But it's more complicated than it looks, and downright weird in some
cases:

$ TZ=America/New_York perl -lwe 'use Date::Manip; use 
DateTime::Format::DateManip; $date=ParseDate("1st January 2009"); print $date; 
my $dt = DateTime::Format::DateManip->parse_datetime($date); print $dt->dmy; 
print $dt->hms; print $dt->time_zone_long_name'
2009010100:00:00
31-12-2008
23:00:00
America/New_York
$ TZ=Australia/Sydney perl -lwe 'use Date::Manip; use 
DateTime::Format::DateManip; $date=ParseDate("1st January 2009"); print $date; 
my $dt = DateTime::Format::DateManip->parse_datetime($date); print $dt->dmy; 
print $dt->hms; print $dt->time_zone_long_name'
2009010100:00:00
01-01-2009
00:00:00
America/New_York
$

What's happening is complicated.
DateTime::Format::DateManip->parse_datetime looks up the abbreviation that
the environmental timezone has now.  Ultimately it tags the generated
DateTime object with a timezone that it works out (backwards) from this
abbreviation, which is why Australia/Sydney, with abbreviation "EST",
gets mapped to America/New_York.  There's one bug.

The DateTime isn't created with that timezone to start with, though.
It's initially created with a fixed-offset timezone, for an offset worked
out (again backwards) from the timezone abbreviation.  This offset is
what determines the interpretation of the (zoneless) calendar time that
you input.  The DateTime then gets switched to its final timezone,
retaining the same absolute point in time.  If the timezone has a
different offset now from what the current abbreviation suggested, this
causes a change in how the point in time is expressed.  Second bug here.

Part of this is actually the fault of Date::Manip, which
DateTime::Format::DateManip calls out to.  For example, this is utterly
the wrong timezone:

$ TZ=Australia/Sydney perl -MDate::Manip=UnixDate -lwe 'print 
UnixDate("2009010100:00:00", "%Y-%m-%d %T %z %Z")'
2009-01-01 00:00:00 -0500 EST
$

And here the offset and abbreviation are for the current time, not the
time being displayed:

$ TZ=Europe/London perl -MDate::Manip=UnixDate -lwe 'print 
UnixDate("2009010100:00:00", "%Y-%m-%d %T %z %Z")'   
2009-01-01 00:00:00 +0100 BST
$

DateTime::Format::DateManip shares some of the blame, though, for
going through such complicated gyrations with timezones.  Using the two
timezones and shifting between them is a definite bug.  It looks like it
would work correctly if it just told DateTime to use the local timezone
and didn't ask Date::Manip about timezones at all.

>I suppose I should go look at the internals and submit a patch?

Go patch DT::F::DM, yes.  Date::Manip seems to be beyond help.

-zefram


Re: adding a time to a DT object

2008-09-25 Thread Zefram
Perrin Harkins wrote:
>I tried making a Strptime object with pattern '%I %p' but that seems
>to just return undef when I try to parse these times.

%I only does the hour.  You'd need "%I:%M %p" to parse the string you
gave.  If the input is from a human, though, DT::F::Natural might be a
better choice: you don't have to tell it what the format is.

>   It looks like
>maybe I want a DateTime::Incomplete,

You could use one to represent the time-of-day on its own, and that class
then provides a method to merge it with the date to make a complete
DateTime.  You don't win much from this.  I think it's only useful if
the parts you're merging don't always have the same boundary.  Say, if
you sometimes want to include the day-of-month in your time-of-day string.

> but it's not clear how to make
>one from a string.

Yes.  DT::F::Strptime is logically an appropriate tool for this,
but it doesn't actually provide output in DT::Incomplete form.
Likewise DT::F::Natural.

>And it also looks like maybe I want a
>DateTime::Duration since I want to add it to my DT object,

No.  That would be appropriate if you were starting from a base time
and adding on a duration (such as 10 hours and 30 minutes).  That's not
what you're doing.  You're constructing the representation of a point
in time by taking parts of that representation from different places.
It's a piecewise construction, not an addition of complete entities.

>I ended up parsing the string myself and calling set methods on the
>DT, but that seems pretty lame with all of these classes available.

It's not a bad way, especially if the string format is fixed.  With
DT::F::Strptime doing its bit, you still need to use the set methods to
copy the time-of-day from one DT object to another (since DT::Incomplete
isn't doing that for you).

-zefram


Re: I'd tell you how long it'll take for my hair to fall out but I'm struggling with duration!

2008-10-01 Thread Zefram
Kristian Flint wrote:
>definitely a fixed number of seconds in a day

Leap seconds.  DateTime doesn't really do them correctly -- actually its
time scale is a bit schizophrenic at the sub-second level -- but it does
represent leap seconds that it knows about.  So some days, about one in
500, have 86401 seconds.

>  (actually unless we're
>talking about that tiny fraction that ends up counting towards a quarter
>of a day per year?).

No.  That's a leap day occuring roughly every four years, which means
that there isn't a fixed number of days per year.

>This doesn't actually return what's stated above (#) but actually
>returns
>0 years, 41 months, 1 days, 00 hours, 375 minutes, 45 seconds

Hmm, yes.  DateTime::Duration is normalising to that form.
DT::F::Duration is only showing what it gets from DT::Duration.
Looks like DT::F::Duration is trying to normalise the other way, back
to what you input, but that's going wrong somewhere.

>And if I try and make the pattern => '%e' to just get the number of
>days, this returns 0!

Returns 1 for me.

-zefram


leap seconds in DateTime

2008-10-01 Thread Zefram
 "floating" timezone.

So, anyway, most users don't need precise leap second handling.  Many,
presumably, do need (approximate) calculations on times in the future
and before 1972.  It is sensible for them that vague-regular-UT is
used in those eras.  But they'd be better served by a *consistent*
vague-regular-UT model.  No one is well served by the mixed model: it's
vague *ir*regular UT, not guaranteeing any of the useful things that
its component models do.  DateTime tries to be everything to everyone,
and suffers from the resulting contradictions.

(Yes, I'm a pedant.)

-zefram


Re: leap seconds in DateTime

2008-10-03 Thread Zefram
Monty, James T wrote:
>Uh-oh, I don't get the same results with DateTime 0.4304 and ActivePerl
>5.8.8 on Windows XP:

In the changelog:

|0.162003-08-06
|
|[ IMPROVEMENTS ]
|
|- The XS code now implements leap second-related calculations.
|  However, this is only used on platforms where we can find a usable
|  finite() or isfinite() function/macro, so it isn't used on Win32.

Looks like the XS picks up the leap second table from leaptab.txt, but
the pure Perl needs to be edited manually and now isn't being tested by
users who have XS functionality.

-zefram


Re: America/Sao_Paolo timezone dying for 2008-10-19

2008-10-07 Thread Zefram
Kevin M. Goess wrote:
>perl  -Ilib -MDateTime -MDateTime::TimeZone -e 'DateTime->new(time_zone
>=> "America/Sao_Paulo", year  => 2008, month => 10, day => 12)'
>Invalid local time for date in time zone: America/Sao_Paulo

Midnight on that date doesn't exist in that timezone, because that's
when DST goes into effect and so the clocks jump ahead to 01:00.

>Upgrading to the very latest version of DateTime::TimeZone (released
>yesterday) shows that the 12th now works, but the library fails on 19th
>of October:

Politicians, eh.  They keep changing DST rules at the last minute.

>Suggested fixes or workarounds?

Depends what you're doing with the DateTime.  Maybe you'd be better
off using the UTC timezone, where there are no DST switches to screw
things up.  Or possibly the floating timezone, if you can't actually
do your calculation in UTC.  If you really need Sao Paulo time, because
either you need to convert between that and something else or you need
to see the DST effects, construct the DateTime with hour=>12, because
no one switches DST at noon.

This problem, of course, is why *almost* no one switches DST exactly
at midnight.

-zefram


Re: How to Compute Hours In a Day?

2008-10-19 Thread Zefram
[EMAIL PROTECTED] wrote:
>How do I compute the number of hours in a day in a specific time zone?

Try this:

use DateTime;
for(my $today = DateTime->new(year=>2007, time_zone=>$ARGV[0]); $today->year == 
2007; ) {
(my $tomorrow = $today->clone)->add(days=>1);
print $today, "  ", ($tomorrow->epoch - $today->epoch)/3600, "\n";
$today = $tomorrow;
}

It ought to be possible to do the interval calculation using
DateTime::Duration instead of ->epoch, but the behaviour of DT::D is
unreasonably confusing and I couldn't get it to work.  (Strangely, I got
a version that worked fine for America/New_York, where all the days are
integer numbers of hours, but lost the fractional hour of 2007-12-09 in
America/Caracas, which was 24.5 hours long.)

Beware that the above code assumes that
midnight exists every day.  We recently had a thread
<http://www.nntp.perl.org/group/perl.datetime/2008/10/msg7086.html> about
midnight not existing in some timezones, specifically America/Sao_Paulo.

-zefram


Re: How to Compute Hours In a Day?

2008-10-20 Thread Zefram
[EMAIL PROTECTED] wrote:
>    C:\>perl oddhours.pl 2007
>    2007-12-09  24.5  America/Caracas
>    2007-03-11  22    America/Indiana/Winamac
>    2007-03-25  24.5  Australia/Lord_Howe
>    2007-10-28  23.5  Australia/Lord_Howe

Interesting, Australia/Lord_Howe does a half-hour DST shift every year.

>    1961-01-01  23.74583  Africa/Dar_es_Salaam

That's the switch from an unaligned +02:44:45 to the regular +03:00.
Curiously, it went to +02:44:45 *from* +03:00 earlier, in 1948, and
before that (until 1931) it had used +02:37:08.  Strong odour of politics.

>    C:\>perl oddhours.pl 1967
>    Invalid local time for date in time zone: Africa/Casablanca

Wow, someone *does* switch at noon.  Or did, once.  A quick look at the
tzdata files shows a handful of other noon switches, all one-offs.

I think you're going to have to give up the idea of using a fixed local
time each day as your reference point.  You need a function that finds
the earliest point that lies within a given calendar date in local time,
which will usually (but not always) be labelled 00:00.  It's probably
safer to work in UTC and convert to local, rather than work with local
time-of-day directly, to avoid trouble with ambiguous times of day
(there might be two instances of local 00:00 in one day).

This is still assuming that the extent of each local calendar day
is contiguous.  If the clocks go back an hour at 00:30 local time,
it would break your model.  The Alaskan zones stretch the model to
the limit, having in 1867 a jump back by 24 hours at 24:00 local time,
yielding a 48-hour day.  (Subjectively it was treated as two consecutive
Fridays, which would have had the same calendar date were it not for the
simultaneous switch from the Julian calendar to the Gregorian calendar.)
There are also some Pacific islands that have jumped across the IDL in
the other direction, skipping a calendar date altogether.

Anyway, have a go with this function:

sub day_start() {
my($zone, $y, $m, $d) = @_;
my $p = DateTime->new(time_zone=>"UTC", year=>$y, month=>$m, day=>$d, 
hour=>12);
(my $rd) = $p->utc_rd_values;
while(do { (my $l = $p->clone)->set_time_zone($zone); 
[$l->local_rd_values]->[0] } >= $rd) {
$p->subtract(hours=>1);
}
while(do { (my $l = $p->clone)->set_time_zone($zone); 
[$l->local_rd_values]->[0] } < $rd) {
$p->add(hours=>1);
}
my $q;
while(1) {
($q = $p->clone)->subtract(minutes=>1);
last if do { (my $l = $q->clone)->set_time_zone($zone); 
[$l->local_rd_values]->[0] } < $rd;
$p = $q;
}
while(1) {
($q = $p->clone)->subtract(seconds=>1);
last if do { (my $l = $q->clone)->set_time_zone($zone); 
[$l->local_rd_values]->[0] } < $rd;
$p = $q;
}
return $p;
}

-zefram


Re: Getting a named DateTime::TimeZone from an offset

2008-12-24 Thread Zefram
Randy J. Ray wrote:
>I get a the timezone as a DateTime::TimeZone::OffsetOnly object. But I'd really
>like the "real" timezone, the one I can get a name or a short-name for.

You can't.  They're hopelessly ambiguous.

-zefram


Re: DateTime performance

2009-02-14 Thread Zefram
arie.ha...@gmail.com wrote:
>Our project requres getting time-zone offset for the given time-zone
>id at the current time.

You can speed things up a bit by using the timezone modules in isolation.
You can construct a fake DateTime class, which only provides the methods
->utc_rd_as_seconds and ->utc_year.  Use that class to construct an
object representing the current time.  Then call ->offset_for_datetime
on a timezone object, passing in your fake DateTime.

-zefram


DT::Locale signature breakage

2009-02-18 Thread Zefram
There is a bug in Module::Signature which is causing false signature
verification failures for DateTime::Locale.  The bug is that it attempts
to canonicalise line endings of text files for checksumming, but it does
so in a way that doesn't work on perl 5.6.  The practical upshot is that
if you have \r\n line endings then a signature generated with perl 5.8+
will fail to verify on perl 5.6.  All the XML files in tools/t/test-data
have \r\n line endings, and so suffer this problem.

There's an easy workaround: don't use \r\n.  If all the files are changed
to use \n, like the rest of the distribution, then checksums will be
computed consistently.

Of course, the principle of signing something other than what's
actually distributed is a stupid idea that opens up security
holes.  I have mentioned this to the M::S author, along with the
inconsistent-canonicalisation bug.

-zefram


  1   2   3   >