choptastic / qdate

Erlang date, time, and timezone management: formatting, conversion, and date arithmetic
http://sigma-star.com/blog/post/qdate
MIT License
247 stars 82 forks source link

Best way to get the raw timezone #9

Open rvangraan opened 10 years ago

rvangraan commented 10 years ago

Hi there,

What would be the best approach to add a function that returns the timezone for a given date (string or otherwise)? I need:

"2001-11-12T10:05:01Z" --> "GMT"
"2001-11-12T10:05:01EST" --> "EST"

Ultimately it is about parsing whatever date and timezone into its components and timezone so that it can be maintained. I don't want the implicit conversion of the timezone when using to_date/1. The plan was to use this bit of code to do that:

to_date(ToTZ, Disambiguate, RawDate)  ->
    {ExtractedDate, ExtractedTZ} = extract_timezone(RawDate),
    {RawDate3, FromTZ} = case try_registered_parsers(RawDate) of
        undefined ->
            {ExtractedDate, ExtractedTZ};
        {ParsedDate,undefined} ->
            {ParsedDate,ExtractedTZ};
        {ParsedDate,ParsedTZ} ->
            {ParsedDate,ParsedTZ}
    end,    

Thanks,

Rudolph

choptastic commented 10 years ago

Hi Rudolph,

Thanks for posting this. Right now, you're correct, there's no way to do this. Not that I don't want it to do that, only that I hadn't really considered it.

Perhaps a new option could be added, such that the argument Disambiguate could morph into something like Options, with one option being something like retain_timezone or (hopefully) something more appropriately named.

Then, that way, we could ensure that any conversion will retain any timezone specific information for users that wish to retain it.

You would then be able to call something like:

qdate:to_string("T", [retain_timezone], "2001-11-12T10:05:01Z") to get "GMT".

I'm open to a PR if you're feeling up to it :)

rvangraan commented 10 years ago

I've managed to do it. But I am thinking of the bigger problem. Technically speaking, Erlang's date/time is broken because it is: a) not labeled as a date/time, just a simple 3-tuple each. This makes it hard to figure out if you actually have a date or time. Same thing with now(). b) It does not carry information about the time zone.

So for the last while I've been working to fix that and wrapping Erlang's calendar, date and time stuff into a new library with new types that are properly tagged. My current implementation uses records, one each for date, time and datetime. And I've ported calendar so that it works with the new types and with existing types. All I had to do was add support for timezones and that was why I started playing with your library.

I am very tempted to see if you'd be open to merging the two and then we publish one date/time library that solves the problem. I'm not quite done. But it it works. You can look here to see what I am attempting to do:

https://github.com/rvangraan/datetime/tree/timezone-integration

The import part is the cal.erl, which is a copy of calendar.erl's interface and with support for the new records. It is not done, I need to add full support for the timezone conversions.

The records look like this: https://github.com/rvangraan/datetime/blob/timezone-integration/include/datetime.hrl

What do you think? In my code I've called my new qdate:datetime_info/2 like so:

to_datetime(DateTime) ->
    Disamb = prefer_standard,
    DateTimeInfo = qdate:datetime_info(Disamb,DateTime),
    #datetime{ date = date:new(DateTimeInfo), time = time:new(DateTimeInfo)}.

That results in a map of properties like so:

(muddle_be@wyemac)148> qdate:datetime_info(prefer_standard,"3/1/2013 1:00am CST").
#{date => {2013,3,1},offset => -360,time => {1,0,0},tz => "CST"}

You'll also see that I've been trying to add eunit tests as much as possible in my code.

rvangraan commented 10 years ago

Another thought... I am very tempted to get rid of the records altogether and switch to maps. that way, you'd end up with something like this #{date = {2014,1,9}} for a simple date #{date = {2014,1,9}, time = {12,24,18,870}}, tz="UTC" for a date and time to the millisecond level with timezone

This will have the benefit of solving this problem:

(muddle_be@wyemac)15> qdate:to_date("2001/01/02").
{{2001,1,2},{0,0,0}}
(muddle_be@wyemac)16> qdate:to_date("10:10:10").
{{1970,1,1},{10,10,10}}

The first is just a date, no time. The second is just a time, no date. I think it is wrong to invent artificial values for that. With the map approach they'd just be missing.

I am of two minds on whether one should tag the individual date fields with hour, min, sec etc. if you do it this way. Or perhaps I should stick to records for the components.

What do you think?

choptastic commented 10 years ago

Very interesting. I'm pretty okay with the idea of using an internal format for A Better Date Object (TM), and records are a solid and safe way to go with this route. I'm definitely open to merging in functionality to make the date components always available in some raw format (like a record containing all the details), while retaining the to_string/unixtime/now/date for convenience.

I have to disagree on the maps, though. I prefer the idea of going with records over maps mainly for backwards compatibility. Beyond the syntax, the only thing maps would do to improve our situation right now would be an easier upgrade process (if users were storing the raw parsed date format instead of something more "portable").

What would be your thoughts on this, merging in your datetime lib, and adding it as one more output function (like, say to_qdate()), which could then be the "Native qdate record", and passed into any of the other functions the same way.

The work that you've done so far on datetime looks very nice, and feels to me like the next logical step to improving things overall.

choptastic commented 10 years ago

Oh, and as far as storing the date and time as 3-tuples or as individual fields (hour, minute, day, etc), my gut right now is telling me individual fields is better for one reason: support for subsecond accuracy gets hairy because then you're pattern matching against a 3 or 4-tuple with a microsecond accuracy as the 4th element (for iso8601 subsecond stuff, which ec_date supports, but which I've not yet modified qdate to handle), and the code just feels like it'll be nasty dealing with that.

choptastic commented 10 years ago

Any thoughts on this?

ElectronicRU commented 8 years ago

Bumping this, as I'd really like to be able to extract timezone information from a string.

lukyanov commented 7 years ago

@choptastic Here is my PR regarding the subject: https://github.com/choptastic/qdate/pull/28