While I wasn't looking, Doug Purdy, who is the PM for the team in charge of the XmlSerializer has posted this little cryptic answer to my initial question:
String serializedDateTime = XmlConvert.ToString( someDateTime );
DateTime deserializedDateTime = XmlConvert.ToDateTime( serializedDateTime );
What Doug is showing is really what the serializer is doing with dates under the hood and that I am incorrectly blaming the XmlSerializer for the lack of UTC support. [XmlConvert.ToString() also does nothing more than calling DateTime.ToString() with the appropriate format string]. As I was already saying in yesterday's post, but what I want to make a bit clearer here again is that the actual problem is the lack of time-zone awareness in DateTime.
So the proper thing more me to do is to ask the base-class library team for time-zone support in the base-class library for Whidbey so that Doug can fix this for us ;)
I posted a little question here, just asking "Why?". For your reading convenience, I pull the code in here once more:
DateTime issued;
[XmlIgnore] public DateTime IssuedUtc { get { return issued; } set { issued = value; } }
[XmlElement("issued")] public DateTime IssuedLocalTime { get { return Issued.ToLocalTime(); } set { Issued = value.ToUniversalTime(); } }
So, indeed, why am I doing this? Well, when I decided to normalize all times handled by the backend engine of dasBlog into the UTC timezone, I really didn't think of the XML Serializer being a problem at first. It turned out to be one.
The reason why I wanted all times to be handled internally as UTC is quite simple: Too many time zones to deal with and I need to have a proper reference to do forward and backward time calculations. dasBlog deals with four time-zones:
- "Reader Time": The "<%userWhen%> macro emits a block of script that will cause the browser to emit the time of a post local to the reader's time zone. That one is easy, because the calculation happens on the client, but I need to feed it UTC (GMT).
- "Display Time": This is the time zone the blog owner selects for his/her blog. All times displayed on the weblog pages are shown in that time zone (complete with the TZ name and the GMT offset). This also applies to the "admin pages" such as referrals and events (which both roll over in synch with the display time zone). Display time is calculated dynamically and you will notice that it also automatically adjusts for daylight savings time. The display time zone is also by no means fixed. If the blog author travels (and you will see this on my blog starting next week), he/she can adjust the blog to his/her present time zone. When I am going to be at TechEd Malaysia, my blog will show UTC+0800. To make this time-zone shifting work, the absolute time must be stored in UTC.
- "Engine Time": This is UTC. All of the dasBlog runtime handles everything in UTC.
- "Server Time": Now were getting to the point. The engine runs on a server that has it's own local time zone setting: "server local time". That's something that the user who's running his engine at some ISP can't control and that's the one of all the time zones that really nobody is ever interested in. You shouldn't care whether your blog is hosted in Germany, the U.S. East Coast or Singapore. That's even more of an issue because one could expect that hosted sites may get moved around between ISP locations. The only little thing we're interested in is that the server knows its offset to UTC.
So ... I was thinking.... ask for DateTime.Now.ToUniversalTime(), handle everything in UTC from there on and everything's good. (Btw, I know about DateTime.UtcNow, but I like the expressiveness of this better).
What I wasn't considering is the way the XmlSerializer works, which I am using both for storage and for the various web services (including the Atom feed). What I also found is that the DateTime class in the framework isn't time-zone aware.
The workaround above is used because the XML Serialization infrastructure always assumes local time ("Server time") for serialization and will always emit ISO 8601 dates with a time-zone suffix like this: 2003-08-19T15:15:58.0781250+02:00. So when you are handling UTC times internally and use them blindly with XML serialization for both storage and web services, the serializer will assume you are using local time and throw you off by your own time-zone difference to UTC.
This isn't "too bad" when you store stuff in local XML files, because when you write something wrong from the same place you read it back into and your time-zones don't change you are in ok in memory, but you are nevertheless wrong on disk. What happened to me in dasBlog version 1.1 was that I was thinking that I stored UTC, but in fact I stored everything in local time. My UTC time 2003-08-19T15:15:58 always turned into 2003-08-19T15:15:58+02:00, because the DateTime class doesn't keep time-zone information around that the serializer could use. Therefore the serializer must always assume local time and that causes the offset to be emitted. That's of course much worse for UTC+12.
The fix:
The field DateTime issued; holds the "engine time", which is always UTC.
The property
[XmlIgnore] public DateTime IssuedUtc { get { return issued; } set { issued = value; } }
wraps this value and is the property that the engine works with internally. The XmlSerializer is instructed to ignore this value in the serialization process by declaration of the [XmlIgnore] attribute. Instead, we tell the serializer to look at the following property, which is not used by the engine itself and also indicates that by its name suffix "LocalTime", which essentially declares it as "off limits" for direct access to everyone knowing the project:
[XmlElement("issued")] public DateTime IssuedLocalTime { get { return Issued.ToLocalTime(); } set { Issued = value.ToUniversalTime(); } }
This property is the one that the serializer grabs and it does the proper conversion to and from server local time that the serializer requires. The actual dasBlog code is using a variant of this property that is a bit larger in code size because it also checks for DateTime.MinValue and DateTime.MaxValue occurrences, which, depending on the server time zone, would cause the time zone shifting to fail with an overflow/underflow exception (and no, I am not checking near MinValue and MaxValue):
[XmlElement("Date")]
public DateTime DateLocalTime { get { return (DateUtc==DateTime.MinValue||DateUtc==DateTime.MaxValue)? DateUtc:DateUtc.ToLocalTime(); } set { DateUtc = (value==DateTime.MinValue||value==DateTime.MaxValue)? value:value.Date.ToUniversalTime(); } }
So, that's why.
Be aware that all of this applies to ASP.NET Web Services, too, and if you are dealing with multiple time zones are you are using UTC normalized times in your app, you will have to deal with this. If "server time" makes you happy, you won't need to worry.
Ok, I couldn't resist getting the Arabic support (mostly) right. Malek helped me and by what he tells me, we've succeeded mostly. There are a few things left to do around wildly wrapping symbol characters, but that'll be worked out eventually.
So, this little patch updates dasBlog with French, Russian and Arabic, including full right-to-left reading support, which the engine injects appropriately into the <body> tag (dir="RTL") based on a hint in the Arabic resource table.
On the download site I posted add-on packs for French language support and the first steps toward Arabic language support, which are the string tables. My friend Malek Kemmou from Morocco wants to help me figuring out how we can make right-to-left reading work "magically" and without having to rework all the templates. He also did those two translations. Thanks!
Update: I added a first attempt at generic RTL support to my private build and installed it here. If the primary language is set to any of the Arabic language variants, the page will flip into RTL reading. There are some issues around formatting to be worked out, though.
The workspace is maxed out at the current 20 users. I already submitted the form to ask for more users (the largest decimal number it takes is 99 so I went for that) and I really hope that there's someone at the other end of that form. GDN team, are you listening?
Update: Fixed.
I just posted newtelligence dasBlog version 1.2.3230.0 to the GotDotNet
workspace and to the download
section on the dasBlog site.
With this drop, I now have a feature set that makes me happy for my own blog
and which some of you out there hopefully find useful for your own blogs. It’s
been 5 weeks that were a lot of fun and I learned a lot about other user’s
requirements through the feedback that I’ve gotten in the three weeks that
the code has been public now.
And with this drop, I will therefore also stop the feature rush and likely
not post more than fixes or updates to the language files and templates. Don’t
expect significant new features from me in the upcoming weeks. What that
means is clearly that I expect that something happens in the GotDotNet
workspace from people other than me. There’s been a lot of complaining
in the BlogX workspace about BlogX being stalled in terms of features and that
the code-base has been locked and checked out the whole time. So, there you go:
dasBlog is not checked out and open for all workspace members to
change. I am going to be on the road for 3 1/2 weeks starting Wednesday, so
don’t expect me to organize anything in the workspace or watch it. It’s
yours. I’ll delegate accepting new members and begging to Microsoft for
more workspace resources to someone around here, but that’s about it.
That also means that my private copy I am carrying around on my notebook is
now forked off from the main code base and is going to be my own private little
playground for new things to try.
Even if you are not at all interested in running the software as-is, you may
want to go and grab the code base, because I am sure there’s a lot of
useful little things in there if you are developing ASP.NET apps. It turned out
to be so much new code and so many new little utilities and tricks that I could
hardly keep track of the “new new” things, let alone document them.
There’s a lot of fodder for articles, tips and tricks columns or for highlighting
aspects here on the blog.
One thing you’ll find digging into the code-base is that almost always
when there’s something that doesn’t need to be done synchronously,
I don’t do it synchronously. Every referral gets queued up in an in
memory queue and is written to the referrer-log on a secondary thread. All mail
notifications, pingbacks, trackbacks and the pining of weblogs.com and blo.gs
are also done on secondary threads.
You’ll also find that the little drop-shadows on
the configuration page for the content-filter are neither rendered by IE, not
will you be able to find the displayed images anywhere in your site’s
directories. Instead, the drop shadows are all dynamically rendered. The magic
is done by the “ShadowBox” control that’s in the
newtelligence.Web.UI.WebControls assembly. You can configure the shadow depth,
the background color onto which the shadow is to be rendered, etc.
The DHTML editor is also a reusable component from that assembly. It loads its
toolbar from an embedded resource using the same technique: The
ControlImageHandler class is an IHttpHandler which is capable of redirecting
requests for images back to the controls that rendered the image links. So, in
essence, you get something similar to Windows Forms’ OnPaint() support
for WebControls.
There’s a complete (and working) implementation of a class
WindowsTimeZone, derived from System.TimeZone and a matching WindowsTimeZoneCollection
that grabs all the time zone information known to Windows and exposes that in a
.NET Framework compatible way, including daylight savings time and all that.
The UrlMapper is a generic component that lets you use regular expressions to
filter incoming URLs and map them to internal URLs, which comes very handy if
you change a site’s structure around, MailToWeblog contains a five-liner
showing how to create thumbnails from images, …
…. many things to explain in more detail and so little time. Stay
tuned. ;)
I am sure it's a coindicence, but every once in a while I get referrals from some invisible site inside www.iaea.org.
Therefore, I hereby declare that I support only "Atom the XML format" and not "Atom the bomb". My house is open for inspections, if you guys think that's necessary.
I hope that keeps me out of trouble.
The language list is growing; Tom Mertens and Bart Vermeersch both sent me Dutch string tables for inclusion in dasBlog 1.2. One is going to be the default for Dutch and Dutch (Netherlands) and the other is going to be the default for Dutch (Belgium).
Bedankt! Site updated.

Consider
Norwegian-Bokmal (NO and nb-NO) done. Christian did it. Already
installed here; best seen in the comment view if you’ve got Norwegian as
the most preferred language.
Note: For visitors of your site, this entry is only displayed for users with the preselected language English/English (en) Error: System.ArgumentException: Culture name en;q=1.0 is not supported. Parameter name: name at System.Globalization.CultureInfo..ctor(String name, Boolean useUserOverride) at System.Globalization.CultureInfo.CreateSpecificCulture(String name) at newtelligence.DasBlog.Web.Core.SharedBasePage.SetupPage(Object o, EventArgs e) at System.EventHandler.Invoke(Object sender, EventArgs e) at System.Web.UI.Control.OnInit(EventArgs e) at newtelligence.DasBlog.Web.HomePage.OnInit(EventArgs e) at System.Web.UI.Control.InitRecursive(Control namingContainer) at System.Web.UI.Page.ProcessRequestMain() while processing ht tp://staff.newtelligence.net/clemensv/default.aspx?external_referrer=ht tp://newtelligence.com/.
To whoever caused this event log entry to show up 10 minutes ago .... thanks, should be fixed now ;)
So.... dasBlog 1.2 nears completion and this will then also conclude the “feature rush“ for a while, because the allocated time for this “summer project” (5 weeks) is up and I have to shift gears. The last step for 1.2 is localization. Based on the user’s top preference in the Accept-Language header, dasBlog 1.2 presents all “hard coded” strings for publicly visible elements like the “Comment” link and the entire administrator interface in the preferred language, if the respective resources are available.
There’s now also an additional macro “localString” that you can use to define a multilingual expression in your templates: <%localString(“This is the English default|DE:Das ist die deutsche Alternative|IT:Questo e in italiano”)%>. This will resolve into German or Italian if that’s on the user’s language preferences list (Accept-Language header, again) and fall back to the default expression before the first delimiter if there’s no proper mapping. And, yes, you could also differentiate between “en-GB”, “en-US” and “en-CA”.
On the posting side, all posts are posted with the “invariant culture” (empty identifier) by default and will therefore show up on the blog independent of the user’s language preference. However, you can also select a language/culture pair when posting (for instance either “de” for German in general or “de-AT” for Austria, specifically) and then the post will only show up for users who have this language/culture listed in their browser’s language preferences. In the RSS feeds, the <item> tag will then carry the proper xml:lang designation (which propagates down to all its children as per XML 1.0).
By now, I have two complete language sets for the localized resources for English (en-US) and German (de-DE), but that’s as far as my “active” language skills take me. So if any of you folks out there (especially dasBlog users – hint, hint) have some 20 minutes of time and want to help me with localization into languages other than those two, grab the three files below (it’s just XML), translate the stuff between the <value> tags (except the topmost four) and send them, with the filenames reflecting the culture (such as Web.StringTables.ES.resx for Spanish), back to me at clemensv@newtelligence.com
Of course, I will have to figure out how trust the correctness if I am getting translations into languages that aren’t from Latin or Germanic descent (most of those I can usually figure out), but that’s something to determine if and when it happens. For now, this is also a great experiment to see whether this little call for help yields any results. You’ll definitely get a permanent link and mention prominently on the dasBlog website and I can also put your blog into the blogroll that is included the setup archives, how’s that for a “payment”?
Download: Web.StringTables.resx Download: WebControls.StringTables.resx Download: WebCore.StringTables.resx
Since the backend stores all times in UTC, the engine can also adjust for daylight savings time when rendering. Check out the April 1st page and compare the timezone info below the entries with the following March 30th.
(BTW, I've done the whole time handling stuff mostly because I've seen that being only on GMT is a bit painful if you are a Fast Chicken in NZ. Nic, you get your timezone back.)
Don't be surprised to see this Weblog showing you Russian day and month names if your browser's preferred language is set to Russian or talks to you in Spanish or Italian. Also, don't be surprised if even more things (like the "Comments" link or all the field names on the Comment page) show up in German, if your preferred language is German. That's a feature.
This is a split entry consisting of two posts. Everyone can see this post, but you will not see (2/2) on the website, if your browser's "Languages" setting doesn't have German as an acceptable language.
DateTime issued;
[XmlIgnore] public DateTime IssuedUtc { get { return issued; } set { issued = value; } }
[XmlElement("issued")] public DateTime IssuedLocalTime { get { return Issued.ToLocalTime(); } set { Issued = value.ToUniversalTime(); } }
Why?
Here's a CERT advisory to check out regarding the vulnerability of other DCE implementations to the Blaster worm. And unless otherwise stated on their site (which isn't the case now), I would assume that Software AG's EntireX DCOM is vulnerable as well.
In 1.1 (hidden) and in 1.2 (no longer hidden) of dasBlog, you can enable a feature for Mail-To-Weblog that will automatically turn attached pictures (not embedded pictures) into thumbnails in the post and you can also control the maximum height for the thumbnails. The relevant settings in site.config for 1.1 are
<Pop3InlineAttachedPictures>true</Pop3InlineAttachedPictures> <Pop3InlinedAttachedPicturesThumbHeight>200</Pop3InlinedAttachedPicturesThumbHeight>
In 1.2 the config settings are the same, but I added them to the config page. Here's an example of how it comes out:
 Das Blog-splash.jpg
|