Saturday, 10 August 2024

Calculating DateTime in .NET from Unix timestamp

This article will describe how to calculate datetime in .NET . A Unix timestamp is defined as : - The number of SECONDS that has passed SINCE Unix Epoch, a defined date set to January 1st, 1970 at UTC (Universal Time Coordinated). We can calculate a Unix timestamp for now time if we call the following method:

	DateTimeOffset.UtcNow.ToUnixTimeSeconds()

For example, as of 10th August 2024 at 21:41 this method gives: 1723318757 To calculate the DateTime value from a Unix Timestamp we can define the following extension method, since we will have an interesting issue in 2038 on 19th January 2038 at 03:14:08 UTC. At that time, an overflow will occur for systems using 32-bits integers, which is actually quite a lot of systems included 32-bits time stamps in database systems. A solution of this is to switch to 64-bits long, but this is as stated not an issue until 2038, which is still about 14 years into the future as of 2024.. We will create extension methods for both seconds and milliseconds because sometimes Unix timestamps are given in milliseconds, and we should also support both int and long. In the future, that is 2038, int cannot be used, long must be used. In addition, extension method on string is also added for convenience.


public static class DateTimeExtensions {

	public static DateTime? FromUnixTimeSeconds(this string unixTimestamp) =>
		long.TryParse(unixTimestamp, out long unixTimestampCasted) ? 
			(DateTime?) CalculateDateTimeFromUnixTimeStamp(unixTimestampCasted) : null;

	public static DateTime FromUnixTimeSeconds(this int unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);

	public static DateTime FromUnixTimeSeconds(this long unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);
		
	private static DateTime CalculateDateTimeFromUnixTimeStamp(long unixTimeStamp) =>
		new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimeStamp);

}


Here is how the method could be used in some examples:


    DateTime? dt = "1723318757".FromUnixTimeSeconds();
    DateTime? dt2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString().FromUnixTimeSeconds();
	
	Console.WriteLine(dt.Value);
	Console.WriteLine(dt.Value.Kind);
	Console.WriteLine(dt2);
    

The screenshot below shows how it can be calculated.








Let's next look at an extension method that converts a DateTime to Unix time stamp.


    public static long ToUnixTimeSeconds(this DateTime dt) =>
		CalculateUnixTimeSeconds(dt);

	public static long? ToUnixTimeSeconds(this DateTime? dt) =>
		dt.HasValue ? CalculateUnixTimeSeconds(dt.Value) : null;

	private static long CalculateUnixTimeSeconds(this DateTime dt) =>
		(long) dt.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds;



Usage example:

DateTime? dt4 = DateTime.UtcNow;
Console.WriteLine(dt4.ToUnixTimeSeconds());    

This gives the unix time stamp from current time. Please note that it also possible to cast a DateTime into a DateTimeOffset and then just use built in method ToUnitTimeSeconds


long unixTimestamp5 = ((DateTimeOffset)(dt4)).ToUnixTimeSeconds();
Console.WriteLine(unixTimestamp5);
    

In addition to the methods above, adding support for milliseconds should be fairly straightforward. Officially, Unix time stamps are measured in seconds. And when you convert from a Unix timestamp to a datetime, you will get UTC time. To get the local time you can do this:

DateTime? dt = "1723325214".FromUnixTimeSeconds();
Console.WriteLine(dt.Value.ToLocalTime()); 
    

Use the method ToLocalTime() on the datetime of Kind Utc, i.e. convert from UTC datetime to local datetime, taking your time zone into account. The entire extension method class then looks like this:

DateTimeExtensions.cs



public static class DateTimeExtensions {

	public static long ToUnixTimeSeconds(this DateTime dt) => 
		CalculateUnixTimeSeconds(dt);

	public static long? ToUnixTimeSeconds(this DateTime? dt) =>
		dt.HasValue ? CalculateUnixTimeSeconds(dt.Value) : null;

	private static long CalculateUnixTimeSeconds(this DateTime dt) =>
		(long) dt.ToUniversalTime().Subtract(DateTime.UnixEpoch).TotalSeconds;

	public static DateTime? FromUnixTimeSeconds(this string unixTimestamp) =>
		long.TryParse(unixTimestamp, out long unixTimestampCasted) ? 
			(DateTime?) CalculateDateTimeFromUnixTimeStamp(unixTimestampCasted) : null;

	public static DateTime FromUnixTimeSeconds(this int unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);

	public static DateTime FromUnixTimeSeconds(this long unixTimestamp) =>
		CalculateDateTimeFromUnixTimeStamp(unixTimestamp);
		
	private static DateTime CalculateDateTimeFromUnixTimeStamp(long unixTimeStamp) =>
		new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimeStamp);

}



Friday, 9 August 2024

Converting an UTC datetime into a datetime in an arbitrary time zone

This article will show a method how to convert an UTC datetime into an arbitrary time zone. Here is the extension method for that shown below. The method checks that the specified datetime object has Kind set to Utc, we can't correctly recalculate non UTC date times that might be Local or Unspecified. We can specify the Kind of a DateTime when we specify it for example :

    var someUtcDate = new DateTime(2024, 6, 4, 6, 30, 0, DateTimeKind.Utc); 

We can also use the DateTime.SpecifyKind method:


  var someUtcDate = DateTime.SpecifyKind(someotherDate, DateTimeKind.Utc); 

Please note however, loading datetimes that are not Utc will quickly run into issues when trying to transform a not specified Utc date into another time zone.

DateTimeExtensions.cs


public static class DatetimeExtensions {
    
    /// Returns the DateTime in a specified time zone
    /// 
    ///  must be a recognized time zone id, check with System.TimeZoneInfo.GetSystemTimeZones() available time zones on the target system
    ///  must be a datetime of Kind Utc to be properly calculated into its proper value in the specified time zone id
    /// 
    public static DateTime ConvertToLocalTime(this DateTime dateTime, string timeZoneId){
    
        TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId) ?? System.TimeZoneInfo.GetSystemTimeZones().FirstOrDefault(tz => string.Equals(tz.Id, timeZoneId, StringComparison.InvariantCultureIgnoreCase));
        if (timeZone == null)
        {
            throw new ArgumentException($"The time zone with id {timeZoneId} is not recognized. (Available time zones can be retrieved using 'System.TimeZoneInfo.GetSystemTimeZones()' method)");
        }
        if (dateTime.Kind != DateTimeKind.Utc)
        {
            throw new ArgumentException($"The date time {dateTime} is not of the right Kind 'Utc'");
        }
        return TimeZoneInfo.ConvertTimeFromUtc(dateTime, timeZone);     
    }
    
}


And here is some sample code to test out this method:

Program.cs


void Main()
{
    
    var central = TimeZoneInfo.GetSystemTimeZones().Where(x => x.DisplayName.Contains("Central")).ToList(); 
    central.Dump();
    
    var someUtcDate = new DateTime(2024, 6, 4, 6, 30, 0, DateTimeKind.Utc); 
    var someUtcDateInCST = someUtcDate.ConvertToLocalTime("Central Standard Time"); 
    Console.WriteLine(someUtcDateInCST);
}


This outputs the answer:
04.06.2024 01:30:00

Note that 'Central Standard Time', which is in the Midtwest of USA, is 6 hours behind UTC. But we only are 5 hours behind UTC in the summer half year, since we got daylight saving. Please note, the example time zone used has daylight saving hours, you should use time zones that supports daylight saving to get correct results, in Norway for example we are either two hours or one hour ahead of UTC. Two hours when it is 'summer time' or 'daylight saving hour'. The following image shows the first time zones called something called 'Central' :

Here are the time zones available in Windows 11 systems per default ( I have no custom time zones added ) :

TimeZoneInfo - 141 different time zones in Windows 11 systems (example setup)
(141 items)
IdDisplayName
Dateline Standard Time(UTC-12:00) International Date Line West
UTC-11(UTC-11:00) Coordinated Universal Time-11
Aleutian Standard Time(UTC-10:00) Aleutian Islands
Hawaiian Standard Time(UTC-10:00) Hawaii
Marquesas Standard Time(UTC-09:30) Marquesas Islands
Alaskan Standard Time(UTC-09:00) Alaska
UTC-09(UTC-09:00) Coordinated Universal Time-09
Pacific Standard Time (Mexico)(UTC-08:00) Baja California
UTC-08(UTC-08:00) Coordinated Universal Time-08
Pacific Standard Time(UTC-08:00) Pacific Time (US & Canada)
US Mountain Standard Time(UTC-07:00) Arizona
Mountain Standard Time (Mexico)(UTC-07:00) La Paz, Mazatlan
Mountain Standard Time(UTC-07:00) Mountain Time (US & Canada)
Yukon Standard Time(UTC-07:00) Yukon
Central America Standard Time(UTC-06:00) Central America
Central Standard Time(UTC-06:00) Central Time (US & Canada)
Easter Island Standard Time(UTC-06:00) Easter Island
Central Standard Time (Mexico)(UTC-06:00) Guadalajara, Mexico City, Monterrey
Canada Central Standard Time(UTC-06:00) Saskatchewan
SA Pacific Standard Time(UTC-05:00) Bogota, Lima, Quito, Rio Branco
Eastern Standard Time (Mexico)(UTC-05:00) Chetumal
Eastern Standard Time(UTC-05:00) Eastern Time (US & Canada)
Haiti Standard Time(UTC-05:00) Haiti
Cuba Standard Time(UTC-05:00) Havana
US Eastern Standard Time(UTC-05:00) Indiana (East)
Turks And Caicos Standard Time(UTC-05:00) Turks and Caicos
Paraguay Standard Time(UTC-04:00) Asuncion
Atlantic Standard Time(UTC-04:00) Atlantic Time (Canada)
Venezuela Standard Time(UTC-04:00) Caracas
Central Brazilian Standard Time(UTC-04:00) Cuiaba
SA Western Standard Time(UTC-04:00) Georgetown, La Paz, Manaus, San Juan
Pacific SA Standard Time(UTC-04:00) Santiago
Newfoundland Standard Time(UTC-03:30) Newfoundland
Tocantins Standard Time(UTC-03:00) Araguaina
E. South America Standard Time(UTC-03:00) Brasilia
SA Eastern Standard Time(UTC-03:00) Cayenne, Fortaleza
Argentina Standard Time(UTC-03:00) City of Buenos Aires
Montevideo Standard Time(UTC-03:00) Montevideo
Magallanes Standard Time(UTC-03:00) Punta Arenas
Saint Pierre Standard Time(UTC-03:00) Saint Pierre and Miquelon
Bahia Standard Time(UTC-03:00) Salvador
UTC-02(UTC-02:00) Coordinated Universal Time-02
Greenland Standard Time(UTC-02:00) Greenland
Mid-Atlantic Standard Time(UTC-02:00) Mid-Atlantic - Old
Azores Standard Time(UTC-01:00) Azores
Cape Verde Standard Time(UTC-01:00) Cabo Verde Is.
UTC(UTC) Coordinated Universal Time
GMT Standard Time(UTC+00:00) Dublin, Edinburgh, Lisbon, London
Greenwich Standard Time(UTC+00:00) Monrovia, Reykjavik
Sao Tome Standard Time(UTC+00:00) Sao Tome
Morocco Standard Time(UTC+01:00) Casablanca
W. Europe Standard Time(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
Central Europe Standard Time(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
Romance Standard Time(UTC+01:00) Brussels, Copenhagen, Madrid, Paris
Central European Standard Time(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb
W. Central Africa Standard Time(UTC+01:00) West Central Africa
GTB Standard Time(UTC+02:00) Athens, Bucharest
Middle East Standard Time(UTC+02:00) Beirut
Egypt Standard Time(UTC+02:00) Cairo
E. Europe Standard Time(UTC+02:00) Chisinau
West Bank Standard Time(UTC+02:00) Gaza, Hebron
South Africa Standard Time(UTC+02:00) Harare, Pretoria
FLE Standard Time(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius
Israel Standard Time(UTC+02:00) Jerusalem
South Sudan Standard Time(UTC+02:00) Juba
Kaliningrad Standard Time(UTC+02:00) Kaliningrad
Sudan Standard Time(UTC+02:00) Khartoum
Libya Standard Time(UTC+02:00) Tripoli
Namibia Standard Time(UTC+02:00) Windhoek
Jordan Standard Time(UTC+03:00) Amman
Arabic Standard Time(UTC+03:00) Baghdad
Syria Standard Time(UTC+03:00) Damascus
Turkey Standard Time(UTC+03:00) Istanbul
Arab Standard Time(UTC+03:00) Kuwait, Riyadh
Belarus Standard Time(UTC+03:00) Minsk
Russian Standard Time(UTC+03:00) Moscow, St. Petersburg
E. Africa Standard Time(UTC+03:00) Nairobi
Volgograd Standard Time(UTC+03:00) Volgograd
Iran Standard Time(UTC+03:30) Tehran
Arabian Standard Time(UTC+04:00) Abu Dhabi, Muscat
Astrakhan Standard Time(UTC+04:00) Astrakhan, Ulyanovsk
Azerbaijan Standard Time(UTC+04:00) Baku
Russia Time Zone 3(UTC+04:00) Izhevsk, Samara
Mauritius Standard Time(UTC+04:00) Port Louis
Saratov Standard Time(UTC+04:00) Saratov
Georgian Standard Time(UTC+04:00) Tbilisi
Caucasus Standard Time(UTC+04:00) Yerevan
Afghanistan Standard Time(UTC+04:30) Kabul
West Asia Standard Time(UTC+05:00) Ashgabat, Tashkent
Qyzylorda Standard Time(UTC+05:00) Astana
Ekaterinburg Standard Time(UTC+05:00) Ekaterinburg
Pakistan Standard Time(UTC+05:00) Islamabad, Karachi
India Standard Time(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi
Sri Lanka Standard Time(UTC+05:30) Sri Jayawardenepura
Nepal Standard Time(UTC+05:45) Kathmandu
Central Asia Standard Time(UTC+06:00) Bishkek
Bangladesh Standard Time(UTC+06:00) Dhaka
Omsk Standard Time(UTC+06:00) Omsk
Myanmar Standard Time(UTC+06:30) Yangon (Rangoon)
SE Asia Standard Time(UTC+07:00) Bangkok, Hanoi, Jakarta
Altai Standard Time(UTC+07:00) Barnaul, Gorno-Altaysk
W. Mongolia Standard Time(UTC+07:00) Hovd
North Asia Standard Time(UTC+07:00) Krasnoyarsk
N. Central Asia Standard Time(UTC+07:00) Novosibirsk
Tomsk Standard Time(UTC+07:00) Tomsk
China Standard Time(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi
North Asia East Standard Time(UTC+08:00) Irkutsk
Singapore Standard Time(UTC+08:00) Kuala Lumpur, Singapore
W. Australia Standard Time(UTC+08:00) Perth
Taipei Standard Time(UTC+08:00) Taipei
Ulaanbaatar Standard Time(UTC+08:00) Ulaanbaatar
Aus Central W. Standard Time(UTC+08:45) Eucla
Transbaikal Standard Time(UTC+09:00) Chita
Tokyo Standard Time(UTC+09:00) Osaka, Sapporo, Tokyo
North Korea Standard Time(UTC+09:00) Pyongyang
Korea Standard Time(UTC+09:00) Seoul
Yakutsk Standard Time(UTC+09:00) Yakutsk
Cen. Australia Standard Time(UTC+09:30) Adelaide
AUS Central Standard Time(UTC+09:30) Darwin
E. Australia Standard Time(UTC+10:00) Brisbane
AUS Eastern Standard Time(UTC+10:00) Canberra, Melbourne, Sydney
West Pacific Standard Time(UTC+10:00) Guam, Port Moresby
Tasmania Standard Time(UTC+10:00) Hobart
Vladivostok Standard Time(UTC+10:00) Vladivostok
Lord Howe Standard Time(UTC+10:30) Lord Howe Island
Bougainville Standard Time(UTC+11:00) Bougainville Island
Russia Time Zone 10(UTC+11:00) Chokurdakh
Magadan Standard Time(UTC+11:00) Magadan
Norfolk Standard Time(UTC+11:00) Norfolk Island
Sakhalin Standard Time(UTC+11:00) Sakhalin
Central Pacific Standard Time(UTC+11:00) Solomon Is., New Caledonia
Russia Time Zone 11(UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky
New Zealand Standard Time(UTC+12:00) Auckland, Wellington
UTC+12(UTC+12:00) Coordinated Universal Time+12
Fiji Standard Time(UTC+12:00) Fiji
Kamchatka Standard Time(UTC+12:00) Petropavlovsk-Kamchatsky - Old
Chatham Islands Standard Time(UTC+12:45) Chatham Islands
UTC+13(UTC+13:00) Coordinated Universal Time+13
Tonga Standard Time(UTC+13:00) Nuku'alofa
Samoa Standard Time(UTC+13:00) Samoa
Line Islands Standard Time(UTC+14:00) Kiritimati Island

Tuesday, 23 July 2024

Displaying Emojis in Windows based consoles

This article will show you how you can get started with showing emojis in Windows based consoles. For an initial peek - Here is a video showing the available Emojis on Windows system I found using Nerd Font Caskaydia Cove mono font glyphs :


First off, some code showing how we can output Emojis ! We will list emojis and their code point value, a hexadecimal integer value that is used to get the correct char that represents an Emoji. Here is the console app.
Program.cs


using System.Text;

Console.OutputEncoding = System.Text.Encoding.UTF8;

var sb = new StringBuilder();
int emojisAdded = 0;
const int EMOJIS_PER_LINE = 10;
int codePoint = 0x1F300;

// Define the ranges of emojis
int[][] emojiRanges = new int[][]
{
            new int[] { 0x1F300, 0x1F5FF },
            new int[] { 0x1F600, 0x1F64F },
            new int[] { 0x1F680, 0x1F6FF },
            new int[] { 0x1F900, 0x1F9FF },
            new int[] { 0x1FA70, 0x1FAFF }
};

foreach (var emojiRange in emojiRanges)
{

    for (codePoint = emojiRange[0]; codePoint <= emojiRange[1]; codePoint++)
    {
        string emoji = char.ConvertFromUtf32(codePoint);
        sb.Append(emoji);
        emojisAdded++;
        if (emojisAdded % EMOJIS_PER_LINE == 0)
        {
            Console.WriteLine($"{sb.ToString()} {codePoint - 9:X}- {codePoint:X}");
            sb.Clear();
        }
    }

    Console.WriteLine(sb.ToString() + " " + (codePoint - 9).ToString("X") + (codePoint.ToString("X"))); //print remaining emojis

}


We use some known ranges of code points, hexadecimal valued integers where we know we in UTF-8 will find Emoji. We set up UTF-8 as the Console output encoding. We will use the method char.ConvertFromUtf32 here and the font will render the Emoji as a Glyph. Please note that not all consoles support Emojis in Windows ! We use Windows Terminal and I have set up a font that supports emojis, 'CaskaydiaCove Nerd Font Mono'. You can find the font and .ttf files to install on Github, just search for "Nerd font" and you will find it here :

https://github.com/ryanoasis/nerd-fonts/releases

If we compile the program and open it in Windows Terminal, you should get something similar to what shown below. I see there are issues on the very last emojis as they are not outputted correct, in the group "Symbols and Pictographs Extended-A". Emoticons: U+1F600 to U+1F64F Miscellaneous Symbols and Pictographs: U+1F300 to U+1F5FF Transport and Map Symbols: U+1F680 to U+1F6FF Supplemental Symbols and Pictographs: U+1F900 to U+1F9FF Symbols and Pictographs Extended-A: U+1FA70 to U+1FAFF Here are screenshots from the Emoji demo, ten Emojis are shown per row ! The "code point" integer range for each row is displayed to the right.