/* * 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 . */ 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(@"^ (?[0-9]{4}) (?: [-.]?(?: (?[0-9]{2}) (?: [-.]?(?[0-9]{2}) )? | W(?[0-9]{2}) (?: [-.]?(?[0-9]) )? ) (?: [T ](?[0-9]{2}) (?: :? (?[0-9]{2}) (?: :? (?[0-9]{2}) (? (?Z) | [+-](?[0-9]{2}) (?: :? (?[0-9]{2}) )? )? )? )? )? )? $$", RegexCompileFlags.EXTENDED); s_rfc2822 = new Regex(@"^ (?: [\\s]*(?Mon|Tue|Wed|Thu|Fri|Sat|Sun) , )? [\\s]*(?[0-9]{1,2}) [\\s]+ (?Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [\\s]+ (?[0-9]{4}) [\\s]+ (?[0-9]{2}) : (?[0-9]{2}) (?: : (?[0-9]{2}) )? [\\s]+ (? [+-] (?[0-9]{2}) (?[0-9]{2}) ) $$", RegexCompileFlags.EXTENDED); s_internal = new Regex(@"^ @? (?[0-9]+) [ ](? [+-](?[0-9]{2}) (?: :? (?[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