gitg/libgitg/gitg-date.vala
2014-07-10 15:30:49 +02:00

416 lines
8.2 KiB
Vala

/*
* This file is part of gitg
*
* Copyright (C) 2013 - Jesse van den Kieboom
*
* gitg is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* gitg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gitg. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Gitg
{
public errordomain DateError
{
INVALID_FORMAT
}
public class Date : Object, Initable
{
private static Regex s_rfc2822;
private static Regex s_iso8601;
private static Regex s_internal;
private static Settings? s_gnome_interface_settings;
private static bool s_tried_gnome_interface_settings;
private static string?[] s_months = new string?[] {
null,
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
};
static construct
{
try
{
s_iso8601 = new Regex(@"^
(?<year>[0-9]{4})
(?:
[-.]?(?:
(?<month>[0-9]{2})
(?:
[-.]?(?<day>[0-9]{2})
)?
|
W(?<week>[0-9]{2})
(?:
[-.]?(?<weekday>[0-9])
)?
)
(?:
[T ](?<hour>[0-9]{2})
(?:
:?
(?<minute>[0-9]{2})
(?:
:?
(?<seconds>[0-9]{2})
(?<tz>
(?<tzutc>Z) |
[+-](?<tzhour>[0-9]{2})
(?:
:?
(?<tzminute>[0-9]{2})
)?
)?
)?
)?
)?
)?
$$", RegexCompileFlags.EXTENDED);
s_rfc2822 = new Regex(@"^
(?:
[\\s]*(?<dayofweek>Mon|Tue|Wed|Thu|Fri|Sat|Sun)
,
)?
[\\s]*(?<day>[0-9]{1,2})
[\\s]+
(?<month>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
[\\s]+
(?<year>[0-9]{4})
[\\s]+
(?<hour>[0-9]{2})
:
(?<minute>[0-9]{2})
(?:
:
(?<seconds>[0-9]{2})
)?
[\\s]+
(?<tz>
[+-]
(?<tzhour>[0-9]{2})
(?<tzminute>[0-9]{2})
)
$$", RegexCompileFlags.EXTENDED);
s_internal = new Regex(@"^
@?
(?<timestamp>[0-9]+)
[ ](?<tz>
[+-](?<tzhour>[0-9]{2})
(?:
:?
(?<tzminute>[0-9]{2})?
)
)
$$", RegexCompileFlags.EXTENDED);
}
catch (Error e)
{
warning(@"Failed to compile date regex: $(e.message)");
}
}
private static bool fetch_and_set_int(MatchInfo info, string name, ref int retval)
{
string? val = info.fetch_named(name);
if (val == null)
{
return false;
}
retval = int.parse(val);
return true;
}
private static bool fetch_and_set_double(MatchInfo info, string name, ref double retval)
{
string? val = info.fetch_named(name);
if (val == null)
{
return false;
}
retval = double.parse(val);
return true;
}
private static DateTime parse_internal(MatchInfo info) throws Error
{
string? timestamp = info.fetch_named("timestamp");
int64 unixt = int64.parse(timestamp);
string? tzs = info.fetch_named("tz");
if (tzs != null)
{
var ret = new DateTime.from_unix_utc(unixt);
return ret.to_timezone(new TimeZone(tzs));
}
else
{
return new DateTime.from_unix_local(unixt);
}
}
private static DateTime parse_iso8601(MatchInfo info) throws Error
{
TimeZone tz = new TimeZone.utc();
int year = 0;
int month = 1;
int day = 1;
int hour = 0;
int minute = 0;
double seconds = 0.0;
fetch_and_set_int(info, "year", ref year);
fetch_and_set_int(info, "month", ref month);
fetch_and_set_int(info, "day", ref day);
fetch_and_set_int(info, "hour", ref hour);
fetch_and_set_int(info, "minute", ref minute);
fetch_and_set_double(info, "seconds", ref seconds);
string? tzs = info.fetch_named("tz");
if (tzs != null)
{
tz = new TimeZone(tzs);
}
else
{
tz = new TimeZone.local();
}
return new DateTime(tz, year, month, day, hour, minute, seconds);
}
private static DateTime parse_rfc2822(MatchInfo info) throws Error
{
TimeZone tz;
int year = 0;
int month = 0;
int day = 1;
int hour = 0;
int minute = 0;
double seconds = 0;
fetch_and_set_int(info, "year", ref year);
string? monthstr = info.fetch_named("month");
for (int i = 0; i < s_months.length; ++i)
{
if (s_months[i] != null && s_months[i] == monthstr)
{
month = i;
break;
}
}
if (month == 0)
{
throw new DateError.INVALID_FORMAT("Invalid month specified");
}
fetch_and_set_int(info, "day", ref day);
fetch_and_set_int(info, "hour", ref hour);
fetch_and_set_int(info, "minute", ref minute);
fetch_and_set_double(info, "seconds", ref seconds);
string? tzs = info.fetch_named("tz");
if (tzs != null)
{
tz = new TimeZone(tzs);
}
else
{
tz = new TimeZone.local();
}
return new DateTime(tz, year, month, day, hour, minute, seconds);
}
private DateTime d_datetime;
public string date_string
{
get; construct set;
}
public DateTime date
{
get { return d_datetime; }
}
public bool init(Cancellable? cancellable = null) throws Error
{
MatchInfo info;
if (s_internal.match(date_string, 0, out info))
{
d_datetime = parse_internal(info);
return true;
}
if (s_iso8601.match(date_string, 0, out info))
{
d_datetime = parse_iso8601(info);
return true;
}
if (s_rfc2822.match(date_string, 0, out info))
{
d_datetime = parse_rfc2822(info);
return true;
}
throw new DateError.INVALID_FORMAT("Invalid date format");
}
public Date(string date) throws Error
{
Object(date_string: date);
((Initable)this).init(null);
}
private bool is_24h
{
get
{
if (s_gnome_interface_settings == null && !s_tried_gnome_interface_settings)
{
var source = SettingsSchemaSource.get_default();
s_tried_gnome_interface_settings = true;
var schema_id = "org.gnome.desktop.interface";
if (source != null && source.lookup(schema_id, true) != null)
{
s_gnome_interface_settings = new Settings(schema_id);
}
}
if (s_gnome_interface_settings == null)
{
return false;
}
return s_gnome_interface_settings.get_enum("clock-format") == GDesktop.ClockFormat.24H;
}
}
public string for_display()
{
var dt = d_datetime;
TimeSpan t = (new DateTime.now_local()).difference(dt);
if (t < TimeSpan.MINUTE * 29.5)
{
int rounded_minutes = (int) Math.round((float) t / TimeSpan.MINUTE);
if (rounded_minutes == 0)
{
return _("Now");
}
else
{
return ngettext("A minute ago", "%d minutes ago", rounded_minutes).printf(rounded_minutes);
}
}
else if (t < TimeSpan.MINUTE * 45)
{
return _("Half an hour ago");
}
else if (t < TimeSpan.HOUR * 23.5)
{
int rounded_hours = (int) Math.round((float) t / TimeSpan.HOUR);
return ngettext("An hour ago", "%d hours ago", rounded_hours).printf(rounded_hours);
}
else if (t < TimeSpan.DAY * 7)
{
int rounded_days = (int) Math.round((float) t / TimeSpan.DAY);
return ngettext("A day ago", "%d days ago", rounded_days).printf(rounded_days);
}
else if (dt.get_year() == new DateTime.now_local().get_year())
{
if (is_24h)
{
/* Translators: this is a strftime type date format which is
used when the date is in the current year and uses a 24 hour
clock.*/
return dt.format(_("%b %e, %H:%M"));
}
else
{
/* Translators: this is a strftime type date format which is
used when the date is in the current year and uses a 12 hour
clock.*/
return dt.format(_("%b %e, %I:%M %p"));
}
}
else
{
if (is_24h)
{
/* Translators: this is a strftime type date format which is
used when the date is not in the current year and uses a 24
hour clock.*/
return dt.format(_("%b %e %Y, %H:%M"));
}
else
{
/* Translators: this is a strftime type date format which is
used when the date is not in the current year and uses a 12
hour clock.*/
return dt.format(_("%b %e %Y, %I:%M %p"));
}
}
}
public Date.for_date_time(DateTime dt)
{
d_datetime = dt;
}
public static DateTime parse(string date) throws Error
{
return (new Date(date)).date;
}
}
}
// ex: ts=4 noet