I got fed up with working around all the limitations with date and time in glib and C (time_t, struct timeval, struct tm, GTimeVal, GDate, etc) so I decided to write something new.
The result is GDateTime. It handles dates and times from 1/1/1 to 12/31/9999 on 100-nanosecond intervals. As requested by a few individuals, it is an opaque/boxed type. String parsing is incomplete but the rest should be in good shape.
The glib branch is available here. Or for those with short attention spans, the header gdatetime.h.
You can even use GDateTimes as keys within a GHashTable using g_date_time_hash, g_date_time_equal.
GHashTable *hash = g_hash_table_new_full (g_date_time_hash, g_date_time_equal, g_date_time_free, NULL);
I'm interested in feedback and patches.

Comments (17)
What about when I want to reference 06/06/-6000?
This seems really cool, thanks for your work.
I like the .NET DateTime approach to the matter and would like to see something similar merged in GLib proper; at a first glance, you seem to be missing internationalization all around though, is this intentional?
@cosimic yeah i’m just not there yet, i would love some suggestions from those who are experienced with it. this also only covers the Gregorian calendar just like .NET’s System.DateTime.
@Richard yeah, unfortunately I don’t know really of any system that will handle that. certainly not time_t
it’s a trade off between infinite extensibility and practical use.
Any plans on submitting this to glib?
I’ve made it known in the irc channel, hopefully with some good suggestions and comments from the community it will be in an acceptable shape to merge in soon.
Here’s a bunch of things about the API that came to mind:
- As said on IRC: better make GTimeSpan typedef to a gint64 and supply macros G_TIME_SPAN_SECOND/MINUTE/DAY and G_TIME_SPAN_TO/FROM_SECONDS/MINUTES/DAYS() – that eases working with the code (adding, subtracting etc) and interop with other libraries.
- Should GDateTime instances be constant after creation? That way you couldn’t support g_date_time_add_*() and had to use g_date_time_new_*() instead, but would always be sure about the value of a GDateTime. )Plus, I’m a big fan of constant types.)
- s/g_date_time_humanize/g_date_time_format_for_display/ seems the more glib’y way to say this.
- Is daylight savings a world wide concept? Or do some weird countries have even more than two different times of the year? Is it even worth supporting in an API? Isn’t DST represented as a different timezone? (I know it is in central europe)
- The API is very verbose – less functions would probably be better. Do we really need g_date_time_tomorrow() for example?
- g_date_time_to_timeval() should probably clamp instead of doing nothing when it returns false. This function seems like an excellent case of “oops, forgot to check return value” bugs
- s/g_date_time_parse_exact/g_date_time_parse_with_format/ or so – otherwise I’ll be wondering if g_date_time_parse() wouldn’t create an exact date.
- I got you talked with Juerg about utc vs local handling, so I’m not gonna comment on this.
Will this me do the following, which I’ve had great difficulty doing in Glom’s code:
- Allow me to display a date for humans, always with 4-digit years, in the current locale.
- Interpret that date when the user enters it.
Standard C and C++ are surprisingly bad at this.
Also, can this easily handle just a date and just a time?
While you’re at it, don’t try to mimic .NET System.DateTime. mimic System.DateTimeOffset instead !
@Murray
g_date_time_humanize() should do this (I’ll rename it per Benjamin’s request). I can add an equal parser as well (parse_from_display).
g_date_time_date() will return a new datetime that is set to midnight for the day. alternatively, _today(), _yesterday(), _tomorrow(), work just as well. that should suffice for days without locking you into a separate date-only type.
As for times, i think the similar should be fine (just keep the default 1/1/1 date).
The best Date/Time API out there that I know of is Joda Time:
http://joda-time.sourceforge.net/
DateTime class:
http://joda-time.sourceforge.net/api-release/org/joda/time/DateTime.html
However, it uses fluent interfaces which is fine for languages with object oriented syntax, but would be less intuitive for C.
Thanks for this. I can barely read C, so there’s no way I can meaningfully contribute to the code, but you mentioned only using the gregorian calendar in your comment there.
If the implication was that you’d be interested in adding support for non-gregorian calendars then I can possibly help there – although probably not with actual patches. I’m very familiar with the hijri calendar, which is the one used in arab and other islamic countries (wrote a perl script to convert between them a long time ago), so if you were interested in suppporting it please let me know.
For those following this let me give a short update.
Most of Benjamin’s suggestions have been implemented.
Can you add a function to get the last day of a month or the date being the last date of a month if you pass in a date?
This would also handle leap years too.
02/15/2008 –> 02/29/2008
02/15/2009 –> 02/28/2009
Or either pass in the month and year.
See the private function:
static gint
days_in_month (gint year,
gint month)
Can we make this a public function as g_date_time_days_in_month ?
Suggestion:
Since time_t can be 32 or 64 bits the rounding to the nearest representable value can be larger theb 2037.
time_t g_date_time_to_time_t (GDateTime *datetime)
2037)
—
> else if ( year > 2037 && (sizeof(time_t) == sizeof(guint32)))
The API as it is does not allow proper serialization and deserialization.
I would suggest to change the boolean “is_utc” to a integer “utc_offset”.
When in utc the utc_offset would be 0 other the utc_offset should contain the correct offset in respect to DST.
As an example :
if (d1.is_utc == d2.is_utc)
{
diff = d2-d1;
}
else if (d1.is_utc != d2.is_utc)
{
if (!d1.is_utc) d1.to_utc;
if (!d2.is_utc) d2.to_utc;
diff = d2-d1;
}
Alternatively :
is_utc = (utc_offset == 0);
diff = (d2-d2.utc_offset) – (d1-d1.utc_offset);
1. The API for g_date_time_diff is not clear.
If the difference should be in absolute nanoseconds then the “g_date_time_diff” function
should assure that the difference is made between times with the same “is_utc” flag or as suggested above utc_offset.
2. g_date_time_to_time_t : Should the GDateTime not be converted to UTC before returning the time_t?
3. g_date_time_to_timeval : Should the GDateTime not be converted to UTC before returning the timeval?
This was completely rewritten based on comments from the gnome bugzilla.
See: http://github.com/chergert/gdatetime